Commit 72680e7e authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Fix vuex specs

Adds additional specs for the new vuex
actions and mutations.
parent f032fc31
......@@ -24,7 +24,7 @@ export const getProjectValueStreamStages = (projectPath, valueStreamId) => {
};
// NOTE: legacy VSA request use a different path
// the `requestPath` provides a full url for th request
// the `requestPath` provides a full url for the request
export const getProjectValueStreamStageData = ({ requestPath, stageId, params }) =>
axios.get(`${requestPath}/events/${stageId}`, { params });
......
......@@ -93,9 +93,6 @@ export default {
handleDateSelect(startDate) {
this.setDateRange({ startDate });
},
isActiveStage(stage) {
return stage.slug === this.selectedStage.slug;
},
onSelectStage(stage) {
this.setSelectedStage(stage);
},
......
......@@ -20,12 +20,8 @@ export const fetchValueStreamStages = ({ commit, state }) => {
return getProjectValueStreamStages(fullPath, selectedValueStream.id)
.then(({ data }) => commit(types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS, data))
.catch((error) => {
const {
response: { status },
} = error;
.catch(({ response: { status } }) => {
commit(types.RECEIVE_VALUE_STREAM_STAGES_ERROR, status);
throw error;
});
};
......@@ -45,12 +41,8 @@ export const fetchValueStreams = ({ commit, dispatch, state }) => {
return getProjectValueStreams(fullPath)
.then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
.then(() => dispatch('setSelectedStage'))
.catch((error) => {
const {
response: { status },
} = error;
.catch(({ response: { status } }) => {
commit(types.RECEIVE_VALUE_STREAMS_ERROR, status);
throw error;
});
};
......@@ -94,9 +86,10 @@ export const setSelectedStage = ({ dispatch, commit, state: { stages } }, select
const refetchData = (dispatch, commit) => {
commit(types.SET_LOADING, true);
return dispatch('fetchValueStreams')
return Promise.resolve()
.then(() => dispatch('fetchValueStreams'))
.then(() => dispatch('fetchCycleAnalyticsData'))
.then(() => commit(types.SET_LOADING, false));
.finally(() => commit(types.SET_LOADING, false));
};
export const setDateRange = ({ dispatch, commit }, { startDate = DEFAULT_DAYS_TO_DISPLAY }) => {
......
import { DEFAULT_VALUE_STREAM } from '~/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export const getStageByTitle = (stages, title) =>
......@@ -95,54 +96,6 @@ export const rawData = {
};
export const convertedData = {
stages: [
selectedStage,
{
...planStage,
active: false,
isUserAllowed: true,
emptyStageText:
'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
component: 'stage-plan-component',
slug: 'plan',
},
{
...codeStage,
active: false,
isUserAllowed: true,
emptyStageText:
'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
component: 'stage-code-component',
slug: 'code',
},
{
...testStage,
active: false,
isUserAllowed: true,
emptyStageText:
'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
component: 'stage-test-component',
slug: 'test',
},
{
...reviewStage,
active: false,
isUserAllowed: true,
emptyStageText:
'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
component: 'stage-review-component',
slug: 'review',
},
{
...stagingStage,
active: false,
isUserAllowed: true,
emptyStageText:
'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
component: 'stage-staging-component',
slug: 'staging',
},
],
summary: [
{ value: '20', title: 'New Issues' },
{ value: '-', title: 'Commits' },
......@@ -256,3 +209,49 @@ export const transformedProjectStagePathData = [
value: 172800,
},
];
export const selectedValueStream = DEFAULT_VALUE_STREAM;
export const rawValueStreamStages = [
{
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) => ({
...convertObjectPropsToCamelCase(s, { deep: true }),
component: `stage-${s.id}-component`,
}));
......@@ -3,10 +3,27 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/cycle_analytics/store/actions';
import httpStatusCodes from '~/lib/utils/http_status';
import { selectedStage } from '../mock_data';
import { selectedStage, selectedValueStream } from '../mock_data';
const mockRequestPath = 'some/cool/path';
const mockFullPath = '/namespace/-/analytics/value_stream_analytics/value_streams';
const mockStartDate = 30;
const mockRequestedDataActions = ['fetchValueStreams', 'fetchCycleAnalyticsData'];
const mockInitializeActionCommit = {
payload: { requestPath: mockRequestPath },
type: 'INITIALIZE_VSA',
};
const mockSetDateActionCommit = { payload: { startDate: mockStartDate }, type: 'SET_DATE_RANGE' };
const mockRequestedDataMutations = [
{
payload: true,
type: 'SET_LOADING',
},
{
payload: false,
type: 'SET_LOADING',
},
];
describe('Project Value Stream Analytics actions', () => {
let state;
......@@ -22,27 +39,26 @@ describe('Project Value Stream Analytics actions', () => {
state = {};
});
it.each`
action | type | payload | expectedActions
${'initializeVsa'} | ${'INITIALIZE_VSA'} | ${{ requestPath: mockRequestPath }} | ${['fetchCycleAnalyticsData']}
${'setDateRange'} | ${'SET_DATE_RANGE'} | ${{ startDate: 30 }} | ${[]}
${'setSelectedStage'} | ${'SET_SELECTED_STAGE'} | ${{ selectedStage }} | ${[]}
`(
'$action should dispatch $expectedActions and commit $type',
({ action, type, payload, expectedActions }) =>
const mutationTypes = (arr) => arr.map(({ type }) => type);
describe.each`
action | payload | expectedActions | expectedMutations
${'initializeVsa'} | ${{ requestPath: mockRequestPath }} | ${mockRequestedDataActions} | ${[mockInitializeActionCommit, ...mockRequestedDataMutations]}
${'setDateRange'} | ${{ startDate: mockStartDate }} | ${mockRequestedDataActions} | ${[mockSetDateActionCommit, ...mockRequestedDataMutations]}
${'setSelectedStage'} | ${{ selectedStage }} | ${['fetchStageData']} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${['fetchValueStreamStages']} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
`('$action', ({ action, payload, expectedActions, expectedMutations }) => {
const types = mutationTypes(expectedMutations);
it(`will dispatch ${expectedActions} and commit ${types}`, () =>
testAction({
action: actions[action],
state,
payload,
expectedMutations: [
{
type,
payload,
},
],
expectedMutations,
expectedActions: expectedActions.map((a) => ({ type: a })),
}),
);
}));
});
describe('fetchCycleAnalyticsData', () => {
beforeEach(() => {
......@@ -60,7 +76,7 @@ describe('Project Value Stream Analytics actions', () => {
{ type: 'REQUEST_CYCLE_ANALYTICS_DATA' },
{ type: 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS' },
],
expectedActions: [{ type: 'setSelectedStage' }, { type: 'fetchStageData' }],
expectedActions: [],
}));
describe('with a failing request', () => {
......@@ -85,7 +101,7 @@ describe('Project Value Stream Analytics actions', () => {
});
describe('fetchStageData', () => {
const mockStagePath = `${mockRequestPath}/events/${selectedStage.name}.json`;
const mockStagePath = `${mockRequestPath}/events/${selectedStage.name}`;
beforeEach(() => {
state = {
......@@ -153,4 +169,115 @@ describe('Project Value Stream Analytics actions', () => {
}));
});
});
describe('fetchValueStreams', () => {
const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
beforeEach(() => {
state = {
fullPath: mockFullPath,
};
mock = new MockAdapter(axios);
mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
});
it(`commits the 'REQUEST_VALUE_STREAMS' mutation`, () =>
testAction({
action: actions.fetchValueStreams,
state,
payload: {},
expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
expectedActions: [{ type: 'receiveValueStreamsSuccess' }, { type: 'setSelectedStage' }],
}));
describe('with a failing request', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
});
it(`commits the 'RECEIVE_VALUE_STREAMS_ERROR' mutation`, () =>
testAction({
action: actions.fetchValueStreams,
state,
payload: {},
expectedMutations: [
{ type: 'REQUEST_VALUE_STREAMS' },
{ type: 'RECEIVE_VALUE_STREAMS_ERROR', payload: httpStatusCodes.BAD_REQUEST },
],
expectedActions: [],
}));
});
});
describe('receiveValueStreamsSuccess', () => {
const mockValueStream = {
id: 'mockDefault',
name: 'mock default',
};
const mockValueStreams = [mockValueStream, selectedValueStream];
it('with data, will set the first value stream', () => {
testAction({
action: actions.receiveValueStreamsSuccess,
state,
payload: mockValueStreams,
expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: mockValueStreams }],
expectedActions: [{ type: 'setSelectedValueStream', payload: mockValueStream }],
});
});
it('without data, will set the default value stream', () => {
testAction({
action: actions.receiveValueStreamsSuccess,
state,
payload: [],
expectedMutations: [{ type: 'RECEIVE_VALUE_STREAMS_SUCCESS', payload: [] }],
expectedActions: [{ type: 'setSelectedValueStream', payload: selectedValueStream }],
});
});
});
describe('fetchValueStreamStages', () => {
const mockValueStreamPath = /\/analytics\/value_stream_analytics\/value_streams/;
beforeEach(() => {
state = {
fullPath: mockFullPath,
selectedValueStream,
};
mock = new MockAdapter(axios);
mock.onGet(mockValueStreamPath).reply(httpStatusCodes.OK);
});
it(`commits the 'REQUEST_VALUE_STREAM_STAGES' and 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' mutations`, () =>
testAction({
action: actions.fetchValueStreamStages,
state,
payload: {},
expectedMutations: [
{ type: 'REQUEST_VALUE_STREAM_STAGES' },
{ type: 'RECEIVE_VALUE_STREAM_STAGES_SUCCESS' },
],
expectedActions: [],
}));
describe('with a failing request', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockValueStreamPath).reply(httpStatusCodes.BAD_REQUEST);
});
it(`commits the 'RECEIVE_VALUE_STREAM_STAGES_ERROR' mutation`, () =>
testAction({
action: actions.fetchValueStreamStages,
state,
payload: {},
expectedMutations: [
{ type: 'REQUEST_VALUE_STREAM_STAGES' },
{ type: 'RECEIVE_VALUE_STREAM_STAGES_ERROR', payload: httpStatusCodes.BAD_REQUEST },
],
expectedActions: [],
}));
});
});
});
import * as types from '~/cycle_analytics/store/mutation_types';
import mutations from '~/cycle_analytics/store/mutations';
import { selectedStage, rawEvents, convertedEvents, rawData, convertedData } from '../mock_data';
import {
selectedStage,
rawEvents,
convertedEvents,
rawData,
convertedData,
selectedValueStream,
rawValueStreamStages,
valueStreamStages,
} from '../mock_data';
let state;
const mockRequestPath = 'fake/request/path';
......@@ -17,15 +26,15 @@ describe('Project Value Stream Analytics mutations', () => {
it.each`
mutation | stateKey | value
${types.SET_SELECTED_STAGE} | ${'isLoadingStage'} | ${false}
${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
${types.REQUEST_VALUE_STREAM_STAGES} | ${'stages'} | ${[]}
${types.RECEIVE_VALUE_STREAM_STAGES_ERROR} | ${'stages'} | ${[]}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'isLoading'} | ${true}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'stages'} | ${[]}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'hasError'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${'isLoading'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${'hasError'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'isLoading'} | ${false}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'hasError'} | ${true}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_ERROR} | ${'stages'} | ${[]}
${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
${types.REQUEST_STAGE_DATA} | ${'isEmptyStage'} | ${false}
${types.REQUEST_STAGE_DATA} | ${'hasError'} | ${false}
......@@ -44,12 +53,15 @@ describe('Project Value Stream Analytics mutations', () => {
});
it.each`
mutation | payload | stateKey | value
${types.INITIALIZE_VSA} | ${{ requestPath: mockRequestPath }} | ${'requestPath'} | ${mockRequestPath}
${types.SET_SELECTED_STAGE} | ${selectedStage} | ${'selectedStage'} | ${selectedStage}
${types.SET_DATE_RANGE} | ${{ startDate: mockStartData }} | ${'startDate'} | ${mockStartData}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'stages'} | ${convertedData.stages}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'summary'} | ${convertedData.summary}
mutation | payload | stateKey | value
${types.INITIALIZE_VSA} | ${{ requestPath: mockRequestPath }} | ${'requestPath'} | ${mockRequestPath}
${types.SET_DATE_RANGE} | ${{ startDate: mockStartData }} | ${'startDate'} | ${mockStartData}
${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true}
${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false}
${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream}
${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS} | ${rawData} | ${'summary'} | ${convertedData.summary}
${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]}
${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages}
`(
'$mutation with $payload will set $stateKey to $value',
({ mutation, payload, stateKey, value }) => {
......
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