Commit f6233515 authored by Savas Vedova's avatar Savas Vedova

Merge branch '328422-vsa-stage-id-mismatch-in-specs' into 'master'

VSA Stage ID mismatch in specs [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!66358
parents 949caf37 18e04efc
...@@ -17,13 +17,14 @@ import { ...@@ -17,13 +17,14 @@ import {
rawStageMedians, rawStageMedians,
createdBefore, createdBefore,
createdAfter, createdAfter,
deepCamelCase,
} from 'jest/cycle_analytics/mock_data'; } from 'jest/cycle_analytics/mock_data';
import { import {
PAGINATION_TYPE, PAGINATION_TYPE,
PAGINATION_SORT_DIRECTION_DESC, PAGINATION_SORT_DIRECTION_DESC,
PAGINATION_SORT_FIELD_END_EVENT, PAGINATION_SORT_FIELD_END_EVENT,
} from '~/cycle_analytics/constants'; } from '~/cycle_analytics/constants';
import { transformStagesForPathNavigation } from '~/cycle_analytics/utils'; import { transformStagesForPathNavigation, formatMedianValues } from '~/cycle_analytics/utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility'; import { getDatesInRange } from '~/lib/utils/datetime_utility';
...@@ -105,8 +106,6 @@ export const stagingStage = getStageByTitle(dummyState.stages, 'staging'); ...@@ -105,8 +106,6 @@ export const stagingStage = getStageByTitle(dummyState.stages, 'staging');
export const allowedStages = [issueStage, planStage, codeStage]; export const allowedStages = [issueStage, planStage, codeStage];
const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
const stageFixtures = defaultStages.reduce((acc, stage) => { const stageFixtures = defaultStages.reduce((acc, stage) => {
const events = getJSONFixture(fixtureEndpoints.stageEvents(stage)); const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
return { return {
...@@ -115,43 +114,29 @@ const stageFixtures = defaultStages.reduce((acc, stage) => { ...@@ -115,43 +114,29 @@ const stageFixtures = defaultStages.reduce((acc, stage) => {
}; };
}, {}); }, {});
export const stageMedians = rawStageMedians.reduce( const getStageId = (name) => {
(acc, { id, value }) => ({ const { id } = getStageByTitle(dummyState.stages, name);
...acc, return id;
[id]: value, };
export const stageMediansWithNumericIds = formatMedianValues(
rawStageMedians.map(({ id: name, ...rest }) => {
const id = getStageId(name);
return { ...rest, name, id };
}), }),
{},
); );
export const stageMediansWithNumericIds = rawStageMedians.reduce((acc, { id, value }) => {
const { id: stageId } = getStageByTitle(dummyState.stages, id);
return {
...acc,
[stageId]: value,
};
}, {});
export const rawStageCounts = defaultStages.map((id) => ({ export const rawStageCounts = defaultStages.map((id) => ({
id, id,
...getJSONFixture(fixtureEndpoints.stageCount(id)), ...getJSONFixture(fixtureEndpoints.stageCount(id)),
})); }));
// Once https://gitlab.com/gitlab-org/gitlab/-/issues/328422 is fixed export const stageCounts = rawStageCounts.reduce((acc, { id: name, count }) => {
// we should be able to use the rawStageCounts for building const id = getStageId(name);
// the stage counts mock data return {
/*
export const stageCounts = rawStageCounts.reduce(
(acc, { id, value }) => ({
...acc, ...acc,
[id]: value, [id]: count,
}), };
{},
);
*/
export const stageCounts = rawStageMedians.reduce((acc, { id, value }) => {
const { id: stageId } = getStageByTitle(dummyState.stages, id);
return { ...acc, [stageId]: value };
}, {}); }, {});
export const issueEvents = deepCamelCase(stageFixtures.issue); export const issueEvents = deepCamelCase(stageFixtures.issue);
...@@ -172,7 +157,7 @@ export const rawCustomStage = { ...@@ -172,7 +157,7 @@ export const rawCustomStage = {
end_event_identifier: 'issue_first_added_to_board', end_event_identifier: 'issue_first_added_to_board',
}; };
export const medians = stageMedians; export const medians = stageMediansWithNumericIds;
export const rawCustomStageEvents = customizableStagesAndEvents.events; export const rawCustomStageEvents = customizableStagesAndEvents.events;
export const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase); export const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
......
...@@ -12,7 +12,7 @@ import { ...@@ -12,7 +12,7 @@ import {
import { import {
allowedStages, allowedStages,
issueStage, issueStage,
stageMedians, stageMediansWithNumericIds,
stageCounts, stageCounts,
basePaginationResult, basePaginationResult,
initialPaginationState, initialPaginationState,
...@@ -212,7 +212,7 @@ describe('Value Stream Analytics getters', () => { ...@@ -212,7 +212,7 @@ describe('Value Stream Analytics getters', () => {
it('returns the transformed data', () => { it('returns the transformed data', () => {
state = { state = {
stages: allowedStages, stages: allowedStages,
medians: stageMedians, medians: stageMediansWithNumericIds,
selectedStage: issueStage, selectedStage: issueStage,
stageCounts, stageCounts,
}; };
......
...@@ -8,8 +8,9 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; ...@@ -8,8 +8,9 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue';
import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants'; import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
import initState from '~/cycle_analytics/store/state'; import initState from '~/cycle_analytics/store/state';
import { selectedStage, convertedEvents as selectedStageEvents } from './mock_data'; import { selectedStage, issueEvents } from './mock_data';
const selectedStageEvents = issueEvents.events;
const noDataSvgPath = 'path/to/no/data'; const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access'; const noAccessSvgPath = 'path/to/no/access';
......
import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { DEFAULT_VALUE_STREAM, DEFAULT_DAYS_IN_PAST } from '~/cycle_analytics/constants'; import { DEFAULT_VALUE_STREAM, DEFAULT_DAYS_IN_PAST } from '~/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
...@@ -6,11 +7,30 @@ import { getDateInPast } from '~/lib/utils/datetime_utility'; ...@@ -6,11 +7,30 @@ import { getDateInPast } from '~/lib/utils/datetime_utility';
export const createdBefore = new Date(2019, 0, 14); export const createdBefore = new Date(2019, 0, 14);
export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST); export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST);
export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
export const getStageByTitle = (stages, title) => export const getStageByTitle = (stages, title) =>
stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {}; stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents: 'projects/analytics/value_stream_analytics/stages',
stageEvents: (stage) => `projects/analytics/value_stream_analytics/events/${stage}`,
};
export const customizableStagesAndEvents = getJSONFixture(
fixtureEndpoints.customizableCycleAnalyticsStagesAndEvents,
);
export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging']; export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
const stageFixtures = defaultStages.reduce((acc, stage) => {
const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
return {
...acc,
[stage]: events,
};
}, {});
export const summary = [ export const summary = [
{ value: '20', title: 'New Issues' }, { value: '20', title: 'New Issues' },
{ value: null, title: 'Commits' }, { value: null, title: 'Commits' },
...@@ -109,50 +129,13 @@ export const convertedData = { ...@@ -109,50 +129,13 @@ export const convertedData = {
], ],
}; };
export const rawEvents = [ export const rawIssueEvents = stageFixtures.issue;
{ export const issueEvents = deepCamelCase(rawIssueEvents);
title: 'Brockfunc-1617160796', export const planEvents = deepCamelCase(stageFixtures.plan);
author: { export const reviewEvents = deepCamelCase(stageFixtures.review);
id: 275, export const codeEvents = deepCamelCase(stageFixtures.code);
name: 'VSM User4', export const testEvents = deepCamelCase(stageFixtures.test);
username: 'vsm-user-4-1617160796', export const stagingEvents = deepCamelCase(stageFixtures.staging);
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/6a6f5480ae582ba68982a34169420747?s=80&d=identicon',
web_url: 'http://gdk.test:3001/vsm-user-4-1617160796',
show_status: false,
path: '/vsm-user-4-1617160796',
},
iid: '16',
total_time: { days: 1, hours: 9 },
created_at: 'about 1 month ago',
url: 'http://gdk.test:3001/vsa-life/ror-project-vsa/-/issues/16',
short_sha: 'some_sha',
commit_url: 'some_commit_url',
},
{
title: 'Subpod-1617160796',
author: {
id: 274,
name: 'VSM User3',
username: 'vsm-user-3-1617160796',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/fde853fc3ab7dc552e649dcb4fcf5f7f?s=80&d=identicon',
web_url: 'http://gdk.test:3001/vsm-user-3-1617160796',
show_status: false,
path: '/vsm-user-3-1617160796',
},
iid: '20',
total_time: { days: 2, hours: 18 },
created_at: 'about 1 month ago',
url: 'http://gdk.test:3001/vsa-life/ror-project-vsa/-/issues/20',
},
];
export const convertedEvents = rawEvents.map((ev) =>
convertObjectPropsToCamelCase(ev, { deep: true }),
);
export const pathNavIssueMetric = 172800; export const pathNavIssueMetric = 172800;
...@@ -251,228 +234,8 @@ export const selectedProjects = [ ...@@ -251,228 +234,8 @@ export const selectedProjects = [
}, },
]; ];
export const rawValueStreamStages = [ export const rawValueStreamStages = customizableStagesAndEvents.stages;
{
title: 'Issue',
hidden: false,
legend: '',
description: 'Time before an issue gets scheduled',
id: 'issue',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:13" dir="auto"\u003eIssue created\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
},
{
title: 'Plan',
hidden: false,
legend: '',
description: 'Time before an issue starts implementation',
id: 'plan',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
},
{
title: 'Code',
hidden: false,
legend: '',
description: 'Time until first merge request',
id: 'code',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:21" dir="auto"\u003eMerge request created\u003c/p\u003e',
},
];
export const valueStreamStages = rawValueStreamStages.map((s) => export const valueStreamStages = rawValueStreamStages.map((s) =>
convertObjectPropsToCamelCase(s, { deep: true }), convertObjectPropsToCamelCase(s, { deep: true }),
); );
// Temporary workaronud until we have relevant backend fixtures endpoints
export const testEvents = [
{
name: 'test',
id: 53,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/53',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
{
name: 'test',
id: 54,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/54',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
];
export const stagingEvents = [
{
name: 'test',
id: 83,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/83',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
{
name: 'test',
id: 84,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/84',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
];
export const reviewEvents = [
{
title: 'My title 98',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '3',
totalTime: { days: 15 },
createdAt: '20 days ago',
url: 'http://localhost/group3/project9/-/merge_requests/3',
state: 'opened',
},
{
title: 'My title 99',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '4',
totalTime: { days: 9 },
createdAt: '19 days ago',
url: 'http://localhost/group3/project9/-/merge_requests/4',
state: 'opened',
},
];
export const issueEvents = [
{
title: 'My title 24',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '3',
totalTime: { days: 2 },
createdAt: '4 days ago',
url: 'http://localhost/group3/project9/-/issues/3',
},
{
title: 'My title 23',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '2',
totalTime: { days: 2 },
createdAt: '5 days ago',
url: 'http://localhost/group3/project9/-/issues/2',
},
];
...@@ -21,16 +21,23 @@ let trackingSpy = null; ...@@ -21,16 +21,23 @@ let trackingSpy = null;
const noDataSvgPath = 'path/to/no/data'; const noDataSvgPath = 'path/to/no/data';
const emptyStateTitle = 'Too much data'; const emptyStateTitle = 'Too much data';
const notEnoughDataError = "We don't have enough data to show this stage."; const notEnoughDataError = "We don't have enough data to show this stage.";
const [firstIssueEvent] = issueEvents; const issueEventItems = issueEvents.events;
const [firstStagingEvent] = stagingEvents; const stagingEventItems = stagingEvents.events;
const [firstTestEvent] = testEvents; const testEventItems = testEvents.events;
const [firstReviewEvent] = reviewEvents; const reviewEventItems = reviewEvents.events;
const [firstIssueEvent] = issueEventItems;
const [firstStagingEvent] = stagingEventItems;
const [firstTestEvent] = testEventItems;
const [firstReviewEvent] = reviewEventItems;
const pagination = { page: 1, hasNextPage: true }; const pagination = { page: 1, hasNextPage: true };
const findStageEvents = () => wrapper.findAllByTestId('vsa-stage-event'); const findStageEvents = () => wrapper.findAllByTestId('vsa-stage-event');
const findPagination = () => wrapper.findByTestId('vsa-stage-pagination'); const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
const findTable = () => wrapper.findComponent(GlTable); const findTable = () => wrapper.findComponent(GlTable);
const findTableHead = () => wrapper.find('thead');
const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title'); const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
function createComponent(props = {}, shallow = false) { function createComponent(props = {}, shallow = false) {
const func = shallow ? shallowMount : mount; const func = shallow ? shallowMount : mount;
...@@ -38,7 +45,7 @@ function createComponent(props = {}, shallow = false) { ...@@ -38,7 +45,7 @@ function createComponent(props = {}, shallow = false) {
func(StageTable, { func(StageTable, {
propsData: { propsData: {
isLoading: false, isLoading: false,
stageEvents: issueEvents, stageEvents: issueEventItems,
noDataSvgPath, noDataSvgPath,
selectedStage: issueStage, selectedStage: issueStage,
pagination, pagination,
...@@ -64,10 +71,10 @@ describe('StageTable', () => { ...@@ -64,10 +71,10 @@ describe('StageTable', () => {
it('will render the correct events', () => { it('will render the correct events', () => {
const evs = findStageEvents(); const evs = findStageEvents();
expect(evs).toHaveLength(issueEvents.length); expect(evs).toHaveLength(issueEventItems.length);
const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text()); const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
issueEvents.forEach((ev, index) => { issueEventItems.forEach((ev, index) => {
expect(titles[index]).toBe(ev.title); expect(titles[index]).toBe(ev.title);
}); });
}); });
...@@ -84,10 +91,10 @@ describe('StageTable', () => { ...@@ -84,10 +91,10 @@ describe('StageTable', () => {
it('will render the correct events', () => { it('will render the correct events', () => {
const evs = findStageEvents(); const evs = findStageEvents();
expect(evs).toHaveLength(issueEvents.length); expect(evs).toHaveLength(issueEventItems.length);
const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text()); const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
issueEvents.forEach((ev, index) => { issueEventItems.forEach((ev, index) => {
expect(titles[index]).toBe(ev.title); expect(titles[index]).toBe(ev.title);
}); });
}); });
...@@ -106,19 +113,20 @@ describe('StageTable', () => { ...@@ -106,19 +113,20 @@ describe('StageTable', () => {
}); });
it('will set the workflow title to "Issues"', () => { it('will set the workflow title to "Issues"', () => {
expect(wrapper.find('thead').text()).toContain('Issues'); expect(findTableHead().text()).toContain('Issues');
}); });
it('does not render the fork icon', () => { it('does not render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(false); expect(findIcon('fork').exists()).toBe(false);
}); });
it('does not render the branch icon', () => { it('does not render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(false); expect(findIcon('commit').exists()).toBe(false);
}); });
it('will render the total time', () => { it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 days'); const createdAt = firstIssueEvent.createdAt.replace(' ago', '');
expect(findStageTime().text()).toBe(createdAt);
}); });
it('will render the author', () => { it('will render the author', () => {
...@@ -143,8 +151,8 @@ describe('StageTable', () => { ...@@ -143,8 +151,8 @@ describe('StageTable', () => {
}); });
it('will set the workflow title to "Merge requests"', () => { it('will set the workflow title to "Merge requests"', () => {
expect(wrapper.find('thead').text()).toContain('Merge requests'); expect(findTableHead().text()).toContain('Merge requests');
expect(wrapper.find('thead').text()).not.toContain('Issues'); expect(findTableHead().text()).not.toContain('Issues');
}); });
}); });
...@@ -157,8 +165,8 @@ describe('StageTable', () => { ...@@ -157,8 +165,8 @@ describe('StageTable', () => {
}); });
it('will set the workflow title to "Deployments"', () => { it('will set the workflow title to "Deployments"', () => {
expect(wrapper.find('thead').text()).toContain('Deployments'); expect(findTableHead().text()).toContain('Deployments');
expect(wrapper.find('thead').text()).not.toContain('Issues'); expect(findTableHead().text()).not.toContain('Issues');
}); });
it('will not render the event title', () => { it('will not render the event title', () => {
...@@ -166,15 +174,15 @@ describe('StageTable', () => { ...@@ -166,15 +174,15 @@ describe('StageTable', () => {
}); });
it('will render the fork icon', () => { it('will render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(true); expect(findIcon('fork').exists()).toBe(true);
}); });
it('will render the branch icon', () => { it('will render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(true); expect(findIcon('commit').exists()).toBe(true);
}); });
it('will render the total time', () => { it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 mins'); expect(findStageTime().text()).toBe('2 mins');
}); });
it('will render the build shortSha', () => { it('will render the build shortSha', () => {
...@@ -199,8 +207,8 @@ describe('StageTable', () => { ...@@ -199,8 +207,8 @@ describe('StageTable', () => {
}); });
it('will set the workflow title to "Jobs"', () => { it('will set the workflow title to "Jobs"', () => {
expect(wrapper.find('thead').text()).toContain('Jobs'); expect(findTableHead().text()).toContain('Jobs');
expect(wrapper.find('thead').text()).not.toContain('Issues'); expect(findTableHead().text()).not.toContain('Issues');
}); });
it('will not render the event title', () => { it('will not render the event title', () => {
...@@ -208,15 +216,15 @@ describe('StageTable', () => { ...@@ -208,15 +216,15 @@ describe('StageTable', () => {
}); });
it('will render the fork icon', () => { it('will render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(true); expect(findIcon('fork').exists()).toBe(true);
}); });
it('will render the branch icon', () => { it('will render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(true); expect(findIcon('commit').exists()).toBe(true);
}); });
it('will render the total time', () => { it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 mins'); expect(findStageTime().text()).toBe('2 mins');
}); });
it('will render the build shortSha', () => { it('will render the build shortSha', () => {
......
...@@ -4,8 +4,8 @@ import * as types from '~/cycle_analytics/store/mutation_types'; ...@@ -4,8 +4,8 @@ import * as types from '~/cycle_analytics/store/mutation_types';
import mutations from '~/cycle_analytics/store/mutations'; import mutations from '~/cycle_analytics/store/mutations';
import { import {
selectedStage, selectedStage,
rawEvents, rawIssueEvents,
convertedEvents, issueEvents,
rawData, rawData,
convertedData, convertedData,
selectedValueStream, selectedValueStream,
...@@ -16,6 +16,8 @@ import { ...@@ -16,6 +16,8 @@ import {
} from '../mock_data'; } from '../mock_data';
let state; let state;
const rawEvents = rawIssueEvents.events;
const convertedEvents = issueEvents.events;
const mockRequestPath = 'fake/request/path'; const mockRequestPath = 'fake/request/path';
const mockCreatedAfter = '2020-06-18'; const mockCreatedAfter = '2020-06-18';
const mockCreatedBefore = '2020-07-18'; const mockCreatedBefore = '2020-07-18';
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
include JavaScriptFixturesHelpers
let_it_be(:value_stream_id) { 'default' }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
let_it_be(:user) { create(:user, :admin) }
let_it_be(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project, created_at: 4.days.ago) }
let(:issue_1) { create(:issue, project: project, created_at: 5.days.ago) }
let(:issue_2) { create(:issue, project: project, created_at: 4.days.ago, milestone: milestone) }
let(:issue_3) { create(:issue, project: project, created_at: 3.days.ago, milestone: milestone) }
let(:mr_1) { create(:merge_request, source_project: project, allow_broken: true, created_at: 5.days.ago) }
let(:mr_2) { create(:merge_request, source_project: project, allow_broken: true, created_at: 4.days.ago) }
let(:pipeline_1) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr_1.source_branch, sha: mr_1.source_branch_sha, head_pipeline_of: mr_1) }
let(:pipeline_2) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr_2.source_branch, sha: mr_2.source_branch_sha, head_pipeline_of: mr_2) }
let(:build_1) { create(:ci_build, :success, pipeline: pipeline_1, author: user) }
let(:build_2) { create(:ci_build, :success, pipeline: pipeline_2, author: user) }
def prepare_cycle_analytics_data
group.add_maintainer(user)
project.add_maintainer(user)
create_commit_referencing_issue(issue_1)
create_commit_referencing_issue(issue_2)
create_merge_request_closing_issue(user, project, issue_1)
create_merge_request_closing_issue(user, project, issue_2)
merge_merge_requests_closing_issue(user, project, issue_3)
end
def create_deployment
deploy_master(user, project, environment: 'staging')
deploy_master(user, project)
end
def update_metrics
issue_1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
issue_2.metrics.update!(first_added_to_board_at: 2.days.ago, first_mentioned_in_commit_at: 1.day.ago)
mr_1.metrics.update!({
merged_at: 5.days.ago,
first_deployed_to_production_at: 1.day.ago,
latest_build_started_at: 5.days.ago,
latest_build_finished_at: 1.day.ago,
pipeline: build_1.pipeline
})
mr_2.metrics.update!({
merged_at: 10.days.ago,
first_deployed_to_production_at: 5.days.ago,
latest_build_started_at: 9.days.ago,
latest_build_finished_at: 7.days.ago,
pipeline: build_2.pipeline
})
end
before(:all) do
clean_frontend_fixtures('projects/analytics/value_stream_analytics/')
end
before do
stub_licensed_features(cycle_analytics_for_groups: true)
prepare_cycle_analytics_data
update_metrics
create_deployment
end
describe Projects::Analytics::CycleAnalytics::StagesController, type: :controller do
render_views
let(:params) { { namespace_id: group, project_id: project, value_stream_id: value_stream_id } }
before do
project.add_developer(user)
sign_in(user)
end
it 'projects/analytics/value_stream_analytics/stages' do
get(:index, params: params, format: :json)
expect(response).to be_successful
end
end
describe Projects::CycleAnalytics::EventsController, type: :controller do
render_views
let(:params) { { namespace_id: group, project_id: project, value_stream_id: value_stream_id } }
before do
project.add_developer(user)
sign_in(user)
end
Gitlab::Analytics::CycleAnalytics::DefaultStages.all.each do |stage|
it "projects/analytics/value_stream_analytics/events/#{stage[:name]}" do
get(stage[:name], params: params, format: :json)
expect(response).to be_successful
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment