Commit 1e511a25 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Martin Wortschack

[VSA] Split Vuex actions into separate files

parent 2c58b606
import Api from 'ee/api';
import { getValueStreamStageMedian } from '~/api/analytics_api';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { FETCH_VALUE_STREAM_DATA, OVERVIEW_STAGE_CONFIG } from '../constants'; import { removeFlash } from '../utils';
import {
removeFlash,
throwIfUserForbidden,
checkForDataError,
flashErrorIfStatusNotOk,
} from '../utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
export * from './actions/filters';
export * from './actions/stages';
export * from './actions/value_streams';
const appendExtension = (path) => (path.indexOf('.') > -1 ? path : `${path}.json`); const appendExtension = (path) => (path.indexOf('.') > -1 ? path : `${path}.json`);
export const setPaths = ({ dispatch }, options) => { export const setPaths = ({ dispatch }, options) => {
...@@ -28,151 +23,6 @@ export const setPaths = ({ dispatch }, options) => { ...@@ -28,151 +23,6 @@ export const setPaths = ({ dispatch }, options) => {
export const setFeatureFlags = ({ commit }, featureFlags) => export const setFeatureFlags = ({ commit }, featureFlags) =>
commit(types.SET_FEATURE_FLAGS, featureFlags); commit(types.SET_FEATURE_FLAGS, featureFlags);
const refreshData = ({ selectedStage, isOverviewStageSelected, dispatch }) => {
if (selectedStage && !isOverviewStageSelected) dispatch('fetchStageData', selectedStage.id);
return dispatch('fetchCycleAnalyticsData');
};
export const setSelectedProjects = (
{ commit, dispatch, getters: { isOverviewStageSelected }, state: { selectedStage } },
projects,
) => {
commit(types.SET_SELECTED_PROJECTS, projects);
return refreshData({ dispatch, selectedStage, isOverviewStageSelected });
};
export const setSelectedStage = ({ commit }, stage) => commit(types.SET_SELECTED_STAGE, stage);
export const setDateRange = (
{ commit, dispatch, getters: { isOverviewStageSelected }, state: { selectedStage } },
{ createdAfter, createdBefore },
) => {
commit(types.SET_DATE_RANGE, { createdBefore, createdAfter });
if (selectedStage && !isOverviewStageSelected) dispatch('fetchStageData', selectedStage.id);
return dispatch('fetchCycleAnalyticsData');
};
export const requestStageData = ({ commit }) => commit(types.REQUEST_STAGE_DATA);
export const receiveStageDataError = ({ commit }, error) => {
const { message = '' } = error;
flashErrorIfStatusNotOk({
error,
message: __('There was an error fetching data for the selected stage'),
});
commit(types.RECEIVE_STAGE_DATA_ERROR, message);
};
export const fetchStageData = ({ dispatch, getters, commit }, stageId) => {
const {
cycleAnalyticsRequestParams = {},
currentValueStreamId,
currentGroupPath,
paginationParams,
} = getters;
dispatch('requestStageData');
return Api.cycleAnalyticsStageEvents({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: {
...cycleAnalyticsRequestParams,
...paginationParams,
},
})
.then(checkForDataError)
.then(({ data, headers }) => {
const { page = null, nextPage = null } = parseIntPagination(normalizeHeaders(headers));
commit(types.RECEIVE_STAGE_DATA_SUCCESS, data);
commit(types.SET_PAGINATION, { ...paginationParams, page, hasNextPage: Boolean(nextPage) });
})
.catch((error) => dispatch('receiveStageDataError', error));
};
export const requestStageMedianValues = ({ commit }) => commit(types.REQUEST_STAGE_MEDIANS);
export const receiveStageMedianValuesError = ({ commit }, error) => {
commit(types.RECEIVE_STAGE_MEDIANS_ERROR, error);
createFlash({
message: __('There was an error fetching median data for stages'),
});
};
const fetchStageMedian = ({ groupId, valueStreamId, stageId, params }) =>
getValueStreamStageMedian({ groupId, valueStreamId, stageId }, params).then(({ data }) => {
return {
id: stageId,
...(data?.error
? {
error: data.error,
value: null,
}
: data),
};
});
export const fetchStageMedianValues = ({ dispatch, commit, getters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams,
activeStages,
currentValueStreamId,
} = getters;
const stageIds = activeStages.map((s) => s.slug);
dispatch('requestStageMedianValues');
return Promise.all(
stageIds.map((stageId) =>
fetchStageMedian({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: cycleAnalyticsRequestParams,
}),
),
)
.then((data) => commit(types.RECEIVE_STAGE_MEDIANS_SUCCESS, data))
.catch((error) => dispatch('receiveStageMedianValuesError', error));
};
const fetchStageCount = ({ groupId, valueStreamId, stageId, params }) =>
Api.cycleAnalyticsStageCount({ groupId, valueStreamId, stageId, params }).then(({ data }) => {
return {
id: stageId,
...(data?.error
? {
error: data.error,
value: null,
}
: data),
};
});
export const fetchStageCountValues = ({ commit, getters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams,
activeStages,
currentValueStreamId,
} = getters;
const stageIds = activeStages.map((s) => s.slug);
commit(types.REQUEST_STAGE_COUNTS);
return Promise.all(
stageIds.map((stageId) =>
fetchStageCount({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: cycleAnalyticsRequestParams,
}),
),
)
.then((data) => commit(types.RECEIVE_STAGE_COUNTS_SUCCESS, data))
.catch((error) => commit(types.RECEIVE_STAGE_COUNTS_ERROR, error));
};
export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_VALUE_STREAM_DATA); export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_VALUE_STREAM_DATA);
export const receiveCycleAnalyticsDataSuccess = ({ commit, dispatch }) => { export const receiveCycleAnalyticsDataSuccess = ({ commit, dispatch }) => {
...@@ -207,49 +57,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => { ...@@ -207,49 +57,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => {
}); });
}; };
export const requestGroupStages = ({ commit }) => commit(types.REQUEST_GROUP_STAGES);
export const receiveGroupStagesError = ({ commit }, error) => {
commit(types.RECEIVE_GROUP_STAGES_ERROR, error);
createFlash({
message: __('There was an error fetching value stream analytics stages.'),
});
};
export const setDefaultSelectedStage = ({ dispatch }) =>
dispatch('setSelectedStage', OVERVIEW_STAGE_CONFIG);
export const receiveGroupStagesSuccess = ({ commit }, stages) =>
commit(types.RECEIVE_GROUP_STAGES_SUCCESS, stages);
export const fetchGroupStagesAndEvents = ({ dispatch, commit, getters }) => {
const {
currentValueStreamId: valueStreamId,
currentGroupPath: groupId,
cycleAnalyticsRequestParams: { created_after, project_ids },
} = getters;
dispatch('requestGroupStages');
commit(types.SET_STAGE_EVENTS, []);
return Api.cycleAnalyticsGroupStagesAndEvents({
groupId,
valueStreamId,
params: {
start_date: created_after,
project_ids,
},
})
.then(({ data: { stages = [], events = [] } }) => {
dispatch('receiveGroupStagesSuccess', stages);
commit(types.SET_STAGE_EVENTS, events);
})
.catch((error) => {
throwIfUserForbidden(error);
return dispatch('receiveGroupStagesError', error);
});
};
export const initializeCycleAnalyticsSuccess = ({ commit }) => export const initializeCycleAnalyticsSuccess = ({ commit }) =>
commit(types.INITIALIZE_VALUE_STREAM_SUCCESS); commit(types.INITIALIZE_VALUE_STREAM_SUCCESS);
...@@ -295,112 +102,3 @@ export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {}) ...@@ -295,112 +102,3 @@ export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {})
return dispatch('initializeCycleAnalyticsSuccess'); return dispatch('initializeCycleAnalyticsSuccess');
}; };
export const receiveCreateValueStreamSuccess = ({ commit, dispatch }, valueStream = {}) => {
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream);
return dispatch('fetchCycleAnalyticsData');
};
export const createValueStream = ({ commit, dispatch, getters }, data) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_CREATE_VALUE_STREAM);
return Api.cycleAnalyticsCreateValueStream(currentGroupPath, data)
.then(({ data: newValueStream }) => dispatch('receiveCreateValueStreamSuccess', newValueStream))
.catch(({ response } = {}) => {
const { data: { message, payload: { errors } } = null } = response;
commit(types.RECEIVE_CREATE_VALUE_STREAM_ERROR, { message, errors, data });
});
};
export const updateValueStream = (
{ commit, dispatch, getters },
{ id: valueStreamId, ...data },
) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_UPDATE_VALUE_STREAM);
return Api.cycleAnalyticsUpdateValueStream({ groupId: currentGroupPath, valueStreamId, data })
.then(({ data: newValueStream }) => {
commit(types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS, newValueStream);
return dispatch('fetchCycleAnalyticsData');
})
.catch(({ response } = {}) => {
const { data: { message, payload: { errors } } = null } = response;
commit(types.RECEIVE_UPDATE_VALUE_STREAM_ERROR, { message, errors, data });
});
};
export const deleteValueStream = ({ commit, dispatch, getters }, valueStreamId) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_DELETE_VALUE_STREAM);
return Api.cycleAnalyticsDeleteValueStream(currentGroupPath, valueStreamId)
.then(() => commit(types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS))
.then(() => dispatch('fetchCycleAnalyticsData'))
.catch(({ response } = {}) => {
const { data: { message } = null } = response;
commit(types.RECEIVE_DELETE_VALUE_STREAM_ERROR, message);
});
};
export const fetchValueStreamData = ({ dispatch }) =>
Promise.resolve()
.then(() => dispatch('fetchGroupStagesAndEvents'))
.then(() => dispatch('fetchStageMedianValues'))
.then(() => dispatch('durationChart/fetchDurationData'));
export const setSelectedValueStream = ({ commit, dispatch }, valueStream) => {
commit(types.SET_SELECTED_VALUE_STREAM, valueStream);
return dispatch(FETCH_VALUE_STREAM_DATA);
};
export const receiveValueStreamsSuccess = (
{ state: { selectedValueStream = null }, commit, dispatch },
data = [],
) => {
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
if (!selectedValueStream && data.length) {
const [firstStream] = data;
return Promise.resolve()
.then(() => dispatch('setSelectedValueStream', firstStream))
.then(() => dispatch('fetchStageCountValues'));
}
return Promise.resolve()
.then(() => dispatch(FETCH_VALUE_STREAM_DATA))
.then(() => dispatch('fetchStageCountValues'));
};
export const fetchValueStreams = ({ commit, dispatch, getters }) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_VALUE_STREAMS);
return Api.cycleAnalyticsValueStreams(currentGroupPath)
.then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
.catch((error) => {
const {
response: { status },
} = error;
commit(types.RECEIVE_VALUE_STREAMS_ERROR, status);
throw error;
});
};
export const setFilters = ({
dispatch,
getters: { isOverviewStageSelected },
state: { selectedStage },
}) => {
return refreshData({ dispatch, isOverviewStageSelected, selectedStage });
};
export const updateStageTablePagination = (
{ commit, dispatch, state: { selectedStage } },
paginationParams,
) => {
commit(types.SET_PAGINATION, paginationParams);
return dispatch('fetchStageData', selectedStage.id);
};
import * as types from '../mutation_types';
const refreshData = ({ selectedStage, isOverviewStageSelected, dispatch }) => {
if (selectedStage && !isOverviewStageSelected) dispatch('fetchStageData', selectedStage.id);
return dispatch('fetchCycleAnalyticsData');
};
export const setSelectedProjects = (
{ commit, dispatch, getters: { isOverviewStageSelected }, state: { selectedStage } },
projects,
) => {
commit(types.SET_SELECTED_PROJECTS, projects);
return refreshData({ dispatch, selectedStage, isOverviewStageSelected });
};
export const setDateRange = (
{ commit, dispatch, getters: { isOverviewStageSelected }, state: { selectedStage } },
{ createdAfter, createdBefore },
) => {
commit(types.SET_DATE_RANGE, { createdBefore, createdAfter });
if (selectedStage && !isOverviewStageSelected) dispatch('fetchStageData', selectedStage.id);
return dispatch('fetchCycleAnalyticsData');
};
export const setFilters = ({
dispatch,
getters: { isOverviewStageSelected },
state: { selectedStage },
}) => {
return refreshData({ dispatch, isOverviewStageSelected, selectedStage });
};
export const updateStageTablePagination = (
{ commit, dispatch, state: { selectedStage } },
paginationParams,
) => {
commit(types.SET_PAGINATION, paginationParams);
return dispatch('fetchStageData', selectedStage.id);
};
import Api from 'ee/api';
import { getValueStreamStageMedian } from '~/api/analytics_api';
import createFlash from '~/flash';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { OVERVIEW_STAGE_CONFIG } from '../../constants';
import { checkForDataError, flashErrorIfStatusNotOk, throwIfUserForbidden } from '../../utils';
import * as types from '../mutation_types';
export const setSelectedStage = ({ commit }, stage) => commit(types.SET_SELECTED_STAGE, stage);
export const setDefaultSelectedStage = ({ dispatch }) =>
dispatch('setSelectedStage', OVERVIEW_STAGE_CONFIG);
export const requestStageData = ({ commit }) => commit(types.REQUEST_STAGE_DATA);
export const receiveStageDataError = ({ commit }, error) => {
const { message = '' } = error;
flashErrorIfStatusNotOk({
error,
message: __('There was an error fetching data for the selected stage'),
});
commit(types.RECEIVE_STAGE_DATA_ERROR, message);
};
export const fetchStageData = ({ dispatch, getters, commit }, stageId) => {
const {
cycleAnalyticsRequestParams = {},
currentValueStreamId,
currentGroupPath,
paginationParams,
} = getters;
dispatch('requestStageData');
return Api.cycleAnalyticsStageEvents({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: {
...cycleAnalyticsRequestParams,
...paginationParams,
},
})
.then(checkForDataError)
.then(({ data, headers }) => {
const { page = null, nextPage = null } = parseIntPagination(normalizeHeaders(headers));
commit(types.RECEIVE_STAGE_DATA_SUCCESS, data);
commit(types.SET_PAGINATION, { ...paginationParams, page, hasNextPage: Boolean(nextPage) });
})
.catch((error) => dispatch('receiveStageDataError', error));
};
export const requestStageMedianValues = ({ commit }) => commit(types.REQUEST_STAGE_MEDIANS);
export const receiveStageMedianValuesError = ({ commit }, error) => {
commit(types.RECEIVE_STAGE_MEDIANS_ERROR, error);
createFlash({
message: __('There was an error fetching median data for stages'),
});
};
const fetchStageMedian = ({ groupId, valueStreamId, stageId, params }) =>
getValueStreamStageMedian({ groupId, valueStreamId, stageId }, params).then(({ data }) => {
return {
id: stageId,
...(data?.error
? {
error: data.error,
value: null,
}
: data),
};
});
export const fetchStageMedianValues = ({ dispatch, commit, getters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams,
activeStages,
currentValueStreamId,
} = getters;
const stageIds = activeStages.map((s) => s.slug);
dispatch('requestStageMedianValues');
return Promise.all(
stageIds.map((stageId) =>
fetchStageMedian({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: cycleAnalyticsRequestParams,
}),
),
)
.then((data) => commit(types.RECEIVE_STAGE_MEDIANS_SUCCESS, data))
.catch((error) => dispatch('receiveStageMedianValuesError', error));
};
const fetchStageCount = ({ groupId, valueStreamId, stageId, params }) =>
Api.cycleAnalyticsStageCount({ groupId, valueStreamId, stageId, params }).then(({ data }) => {
return {
id: stageId,
...(data?.error
? {
error: data.error,
value: null,
}
: data),
};
});
export const fetchStageCountValues = ({ commit, getters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams,
activeStages,
currentValueStreamId,
} = getters;
const stageIds = activeStages.map((s) => s.slug);
commit(types.REQUEST_STAGE_COUNTS);
return Promise.all(
stageIds.map((stageId) =>
fetchStageCount({
groupId: currentGroupPath,
valueStreamId: currentValueStreamId,
stageId,
params: cycleAnalyticsRequestParams,
}),
),
)
.then((data) => commit(types.RECEIVE_STAGE_COUNTS_SUCCESS, data))
.catch((error) => commit(types.RECEIVE_STAGE_COUNTS_ERROR, error));
};
export const requestGroupStages = ({ commit }) => commit(types.REQUEST_GROUP_STAGES);
export const receiveGroupStagesError = ({ commit }, error) => {
commit(types.RECEIVE_GROUP_STAGES_ERROR, error);
createFlash({
message: __('There was an error fetching value stream analytics stages.'),
});
};
export const receiveGroupStagesSuccess = ({ commit }, stages) =>
commit(types.RECEIVE_GROUP_STAGES_SUCCESS, stages);
export const fetchGroupStagesAndEvents = ({ dispatch, commit, getters }) => {
const {
currentValueStreamId: valueStreamId,
currentGroupPath: groupId,
cycleAnalyticsRequestParams: { created_after, project_ids },
} = getters;
dispatch('requestGroupStages');
commit(types.SET_STAGE_EVENTS, []);
return Api.cycleAnalyticsGroupStagesAndEvents({
groupId,
valueStreamId,
params: {
start_date: created_after,
project_ids,
},
})
.then(({ data: { stages = [], events = [] } }) => {
dispatch('receiveGroupStagesSuccess', stages);
commit(types.SET_STAGE_EVENTS, events);
})
.catch((error) => {
throwIfUserForbidden(error);
return dispatch('receiveGroupStagesError', error);
});
};
import Api from 'ee/api';
import { FETCH_VALUE_STREAM_DATA } from '../../constants';
import * as types from '../mutation_types';
export const receiveCreateValueStreamSuccess = ({ commit, dispatch }, valueStream = {}) => {
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream);
return dispatch('fetchCycleAnalyticsData');
};
export const createValueStream = ({ commit, dispatch, getters }, data) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_CREATE_VALUE_STREAM);
return Api.cycleAnalyticsCreateValueStream(currentGroupPath, data)
.then(({ data: newValueStream }) => dispatch('receiveCreateValueStreamSuccess', newValueStream))
.catch(({ response } = {}) => {
const { data: { message, payload: { errors } } = null } = response;
commit(types.RECEIVE_CREATE_VALUE_STREAM_ERROR, { message, errors, data });
});
};
export const updateValueStream = (
{ commit, dispatch, getters },
{ id: valueStreamId, ...data },
) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_UPDATE_VALUE_STREAM);
return Api.cycleAnalyticsUpdateValueStream({ groupId: currentGroupPath, valueStreamId, data })
.then(({ data: newValueStream }) => {
commit(types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS, newValueStream);
return dispatch('fetchCycleAnalyticsData');
})
.catch(({ response } = {}) => {
const { data: { message, payload: { errors } } = null } = response;
commit(types.RECEIVE_UPDATE_VALUE_STREAM_ERROR, { message, errors, data });
});
};
export const deleteValueStream = ({ commit, dispatch, getters }, valueStreamId) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_DELETE_VALUE_STREAM);
return Api.cycleAnalyticsDeleteValueStream(currentGroupPath, valueStreamId)
.then(() => commit(types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS))
.then(() => dispatch('fetchCycleAnalyticsData'))
.catch(({ response } = {}) => {
const { data: { message } = null } = response;
commit(types.RECEIVE_DELETE_VALUE_STREAM_ERROR, message);
});
};
export const fetchValueStreamData = ({ dispatch }) =>
Promise.resolve()
.then(() => dispatch('fetchGroupStagesAndEvents'))
.then(() => dispatch('fetchStageMedianValues'))
.then(() => dispatch('durationChart/fetchDurationData'));
export const setSelectedValueStream = ({ commit, dispatch }, valueStream) => {
commit(types.SET_SELECTED_VALUE_STREAM, valueStream);
return dispatch(FETCH_VALUE_STREAM_DATA);
};
export const receiveValueStreamsSuccess = (
{ state: { selectedValueStream = null }, commit, dispatch },
data = [],
) => {
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
if (!selectedValueStream && data.length) {
const [firstStream] = data;
return Promise.resolve()
.then(() => dispatch('setSelectedValueStream', firstStream))
.then(() => dispatch('fetchStageCountValues'));
}
return Promise.resolve()
.then(() => dispatch(FETCH_VALUE_STREAM_DATA))
.then(() => dispatch('fetchStageCountValues'));
};
export const fetchValueStreams = ({ commit, dispatch, getters }) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_VALUE_STREAMS);
return Api.cycleAnalyticsValueStreams(currentGroupPath)
.then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
.catch((error) => {
const {
response: { status },
} = error;
commit(types.RECEIVE_VALUE_STREAMS_ERROR, status);
throw error;
});
};
import * as actions from 'ee/analytics/cycle_analytics/store/actions/filters';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import testAction from 'helpers/vuex_action_helper';
import { createdAfter, createdBefore, selectedProjects } from 'jest/cycle_analytics/mock_data';
import { allowedStages as stages } from '../../mock_data';
stages[0].hidden = true;
const activeStages = stages.filter(({ hidden }) => !hidden);
const [selectedStage] = activeStages;
describe('Value Stream Analytics actions / filters', () => {
let state;
let stateWithOverview = null;
describe.each`
targetAction | payload | mutations
${actions.setDateRange} | ${{ createdAfter, createdBefore }} | ${[{ type: 'SET_DATE_RANGE', payload: { createdAfter, createdBefore } }]}
${actions.setFilters} | ${''} | ${[]}
`('$action', ({ targetAction, payload, mutations }) => {
beforeEach(() => {
stateWithOverview = { ...state, isOverviewStageSelected: () => true };
});
it('dispatches the fetchCycleAnalyticsData action', () => {
return testAction(targetAction, payload, stateWithOverview, mutations, [
{ type: 'fetchCycleAnalyticsData' },
]);
});
describe('with a stage selected', () => {
beforeEach(() => {
stateWithOverview = { ...state, selectedStage };
});
it('dispatches the fetchStageData action', () => {
return testAction(targetAction, payload, stateWithOverview, mutations, [
{ type: 'fetchStageData', payload: selectedStage.id },
{ type: 'fetchCycleAnalyticsData' },
]);
});
});
});
describe('setSelectedProjects', () => {
describe('with `overview` stage selected', () => {
beforeEach(() => {
stateWithOverview = { ...state, isOverviewStageSelected: () => true };
});
it('will dispatch the "fetchCycleAnalyticsData" action', () => {
return testAction(
actions.setSelectedProjects,
selectedProjects,
stateWithOverview,
[{ type: types.SET_SELECTED_PROJECTS, payload: selectedProjects }],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with non overview stage selected', () => {
beforeEach(() => {
state = { ...state, selectedStage };
});
it('will dispatch the "fetchStageData" and "fetchCycleAnalyticsData" actions', () => {
return testAction(
actions.setSelectedProjects,
selectedProjects,
state,
[{ type: types.SET_SELECTED_PROJECTS, payload: selectedProjects }],
[
{ type: 'fetchStageData', payload: selectedStage.id },
{ type: 'fetchCycleAnalyticsData' },
],
);
});
});
});
});
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { OVERVIEW_STAGE_CONFIG } from 'ee/analytics/cycle_analytics/constants';
import * as actions from 'ee/analytics/cycle_analytics/store/actions/stages';
import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import testAction from 'helpers/vuex_action_helper';
import { createdAfter, createdBefore, currentGroup } from 'jest/cycle_analytics/mock_data';
import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import {
allowedStages as stages,
customizableStagesAndEvents,
endpoints,
valueStreams,
} from '../../mock_data';
const stageData = { events: [] };
const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_FOUND}`);
stages[0].hidden = true;
const activeStages = stages.filter(({ hidden }) => !hidden);
const hiddenStage = stages[0];
const [selectedStage] = activeStages;
const selectedStageSlug = selectedStage.slug;
const [selectedValueStream] = valueStreams;
const mockGetters = {
currentGroupPath: () => currentGroup.fullPath,
currentValueStreamId: () => selectedValueStream.id,
};
jest.mock('~/flash');
describe('Value Stream Analytics actions / stages', () => {
let state;
let mock;
beforeEach(() => {
state = {
createdAfter,
createdBefore,
stages: [],
featureFlags: {},
activeStages,
selectedValueStream,
...mockGetters,
};
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
state = { ...state, currentGroup: null };
});
describe('setSelectedStage', () => {
const data = { id: 'someStageId' };
it(`dispatches the ${types.SET_SELECTED_STAGE} and ${types.SET_PAGINATION} actions`, () => {
return testAction(actions.setSelectedStage, data, { ...state, selectedValueStream: {} }, [
{ type: types.SET_SELECTED_STAGE, payload: data },
]);
});
});
describe('setDefaultSelectedStage', () => {
it("dispatches the 'setSelectedStage' with the overview stage", () => {
return testAction(
actions.setDefaultSelectedStage,
null,
state,
[],
[{ type: 'setSelectedStage', payload: OVERVIEW_STAGE_CONFIG }],
);
});
});
describe('fetchStageData', () => {
const headers = {
'X-Next-Page': 2,
'X-Page': 1,
};
beforeEach(() => {
state = { ...state, currentGroup };
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageData).reply(httpStatusCodes.OK, stageData, headers);
});
it(`commits ${types.RECEIVE_STAGE_DATA_SUCCESS} with received data and headers on success`, () => {
return testAction(
actions.fetchStageData,
selectedStageSlug,
state,
[
{
type: types.RECEIVE_STAGE_DATA_SUCCESS,
payload: stageData,
},
{
type: types.SET_PAGINATION,
payload: { page: headers['X-Page'], hasNextPage: true },
},
],
[{ type: 'requestStageData' }],
);
});
describe('without a next page', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock
.onGet(endpoints.stageData)
.reply(httpStatusCodes.OK, { events: [] }, { ...headers, 'X-Next-Page': null });
});
it('sets hasNextPage to false', () => {
return testAction(
actions.fetchStageData,
selectedStageSlug,
state,
[
{
type: types.RECEIVE_STAGE_DATA_SUCCESS,
payload: { events: [] },
},
{
type: types.SET_PAGINATION,
payload: { page: headers['X-Page'], hasNextPage: false },
},
],
[{ type: 'requestStageData' }],
);
});
});
describe('with a failing request', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageData).replyOnce(httpStatusCodes.NOT_FOUND, { error });
});
it('dispatches receiveStageDataError on error', () => {
return testAction(
actions.fetchStageData,
selectedStage,
state,
[],
[
{
type: 'requestStageData',
},
{
type: 'receiveStageDataError',
payload: error,
},
],
);
});
});
});
describe('receiveStageDataError', () => {
const message = 'fake error';
it(`commits the ${types.RECEIVE_STAGE_DATA_ERROR} mutation`, () => {
return testAction(
actions.receiveStageDataError,
{ message },
state,
[
{
type: types.RECEIVE_STAGE_DATA_ERROR,
payload: message,
},
],
[],
);
});
it('will flash an error message', () => {
actions.receiveStageDataError({ commit: () => {} }, {});
expect(createFlash).toHaveBeenCalledWith({
message: 'There was an error fetching data for the selected stage',
});
});
});
describe('fetchStageMedianValues', () => {
let mockDispatch = jest.fn();
const fetchMedianResponse = activeStages.map(({ slug: id }) => ({ events: [], id }));
beforeEach(() => {
state = { ...state, stages, currentGroup };
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.OK, { events: [] });
mockDispatch = jest.fn();
});
it('dispatches receiveStageMedianValuesSuccess with received data on success', () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[{ type: types.RECEIVE_STAGE_MEDIANS_SUCCESS, payload: fetchMedianResponse }],
[{ type: 'requestStageMedianValues' }],
);
});
it('does not request hidden stages', () => {
return actions
.fetchStageMedianValues({
state,
getters: {
...getters,
activeStages,
},
commit: () => {},
dispatch: mockDispatch,
})
.then(() => {
expect(mockDispatch).not.toHaveBeenCalledWith('receiveStageMedianValuesSuccess', {
events: [],
id: hiddenStage.id,
});
});
});
describe(`Status ${httpStatusCodes.OK} and error message in response`, () => {
const dataError = 'Too much data';
const payload = activeStages.map(({ slug: id }) => ({ value: null, id, error: dataError }));
beforeEach(() => {
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.OK, { error: dataError });
});
it(`dispatches the 'RECEIVE_STAGE_MEDIANS_SUCCESS' with ${dataError}`, () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[{ type: types.RECEIVE_STAGE_MEDIANS_SUCCESS, payload }],
[{ type: 'requestStageMedianValues' }],
);
});
});
describe('with a failing request', () => {
beforeEach(() => {
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.NOT_FOUND, { error });
});
it('will dispatch receiveStageMedianValuesError', () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[],
[
{ type: 'requestStageMedianValues' },
{ type: 'receiveStageMedianValuesError', payload: error },
],
);
});
});
});
describe('receiveStageMedianValuesError', () => {
it(`commits the ${types.RECEIVE_STAGE_MEDIANS_ERROR} mutation`, () =>
testAction(
actions.receiveStageMedianValuesError,
{},
state,
[
{
type: types.RECEIVE_STAGE_MEDIANS_ERROR,
payload: {},
},
],
[],
));
it('will flash an error message', () => {
actions.receiveStageMedianValuesError({ commit: () => {} });
expect(createFlash).toHaveBeenCalledWith({
message: 'There was an error fetching median data for stages',
});
});
});
describe('fetchStageCountValues', () => {
const fetchCountResponse = activeStages.map(({ slug: id }) => ({ events: [], id }));
beforeEach(() => {
state = {
...state,
stages,
currentGroup,
featureFlags: state.featureFlags,
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageCount).reply(httpStatusCodes.OK, { events: [] });
});
it('dispatches receiveStageCountValuesSuccess with received data on success', () => {
return testAction(
actions.fetchStageCountValues,
null,
state,
[
{ type: types.REQUEST_STAGE_COUNTS },
{ type: types.RECEIVE_STAGE_COUNTS_SUCCESS, payload: fetchCountResponse },
],
[],
);
});
});
describe('receiveGroupStagesSuccess', () => {
it(`commits the ${types.RECEIVE_GROUP_STAGES_SUCCESS} mutation'`, () => {
return testAction(
actions.receiveGroupStagesSuccess,
{ ...customizableStagesAndEvents.stages },
state,
[
{
type: types.RECEIVE_GROUP_STAGES_SUCCESS,
payload: { ...customizableStagesAndEvents.stages },
},
],
[],
);
});
});
});
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import * as actions from 'ee/analytics/cycle_analytics/store/actions/value_streams';
import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import testAction from 'helpers/vuex_action_helper';
import { currentGroup } from 'jest/cycle_analytics/mock_data';
import httpStatusCodes from '~/lib/utils/http_status';
import { allowedStages as stages, endpoints, valueStreams } from '../../mock_data';
const mockStartEventIdentifier = 'issue_first_mentioned_in_commit';
const mockEndEventIdentifier = 'issue_first_added_to_board';
const mockEvents = {
startEventIdentifier: mockStartEventIdentifier,
endEventIdentifier: mockEndEventIdentifier,
};
stages[0].hidden = true;
const activeStages = stages.filter(({ hidden }) => !hidden);
const hiddenStage = stages[0];
const [selectedStage] = activeStages;
const selectedStageSlug = selectedStage.slug;
const [selectedValueStream] = valueStreams;
const mockGetters = {
currentGroupPath: () => currentGroup.fullPath,
currentValueStreamId: () => selectedValueStream.id,
};
jest.mock('~/flash');
describe('Value Stream Analytics actions / value streams', () => {
let state;
let mock;
beforeEach(() => {
state = {
stages: [],
featureFlags: {},
activeStages,
selectedValueStream,
...mockGetters,
};
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
state = { ...state, currentGroup: null };
});
describe('setSelectedValueStream', () => {
const vs = { id: 'vs-1', name: 'Value stream 1' };
it('refetches the Value Stream Analytics data', () => {
return testAction(
actions.setSelectedValueStream,
vs,
{ ...state, selectedValueStream: {} },
[{ type: types.SET_SELECTED_VALUE_STREAM, payload: vs }],
[{ type: 'fetchValueStreamData' }],
);
});
});
describe('createValueStream', () => {
const payload = {
name: 'cool value stream',
stages: [
{
...selectedStage,
...mockEvents,
id: null,
},
{ ...hiddenStage, ...mockEvents },
],
};
const createResp = { id: 'new value stream', is_custom: true, ...payload };
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onPost(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, createResp);
});
it(`commits the ${types.REQUEST_CREATE_VALUE_STREAM} and ${types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.createValueStream,
payload,
state,
[
{
type: types.REQUEST_CREATE_VALUE_STREAM,
},
],
[{ type: 'receiveCreateValueStreamSuccess', payload: createResp }],
);
});
});
describe('with errors', () => {
const errors = { name: ['is taken'] };
const message = { message: 'error' };
const resp = { message, payload: { errors } };
beforeEach(() => {
mock.onPost(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_CREATE_VALUE_STREAM} and ${types.RECEIVE_CREATE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(
actions.createValueStream,
payload,
state,
[
{ type: types.REQUEST_CREATE_VALUE_STREAM },
{
type: types.RECEIVE_CREATE_VALUE_STREAM_ERROR,
payload: { message, data: payload, errors },
},
],
[],
);
});
});
});
describe('updateValueStream', () => {
const payload = {
name: 'cool value stream',
stages: [
{
...selectedStage,
...mockEvents,
id: 'stage-1',
},
{ ...hiddenStage, ...mockEvents },
],
};
const updateResp = { id: 'new value stream', is_custom: true, ...payload };
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onPut(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, updateResp);
});
it(`commits the ${types.REQUEST_UPDATE_VALUE_STREAM} and ${types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.updateValueStream,
payload,
state,
[
{ type: types.REQUEST_UPDATE_VALUE_STREAM },
{ type: types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS, payload: updateResp },
],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with errors', () => {
const errors = { name: ['is taken'] };
const message = { message: 'error' };
const resp = { message, payload: { errors } };
beforeEach(() => {
mock.onPut(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_UPDATE_VALUE_STREAM} and ${types.RECEIVE_UPDATE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(actions.updateValueStream, payload, state, [
{ type: types.REQUEST_UPDATE_VALUE_STREAM },
{
type: types.RECEIVE_UPDATE_VALUE_STREAM_ERROR,
payload: { message, data: payload, errors },
},
]);
});
});
});
describe('deleteValueStream', () => {
const payload = 'my-fake-value-stream';
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onDelete(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, {});
});
it(`commits the ${types.REQUEST_DELETE_VALUE_STREAM} and ${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.deleteValueStream,
payload,
state,
[
{
type: types.REQUEST_DELETE_VALUE_STREAM,
},
{
type: types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS,
},
],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with errors', () => {
const message = { message: 'failed to delete the value stream' };
const resp = { message };
beforeEach(() => {
mock.onDelete(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_DELETE_VALUE_STREAM} and ${types.RECEIVE_DELETE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(
actions.deleteValueStream,
payload,
state,
[
{ type: types.REQUEST_DELETE_VALUE_STREAM },
{
type: types.RECEIVE_DELETE_VALUE_STREAM_ERROR,
payload: message,
},
],
[],
);
});
});
});
describe('fetchValueStreams', () => {
beforeEach(() => {
state = {
...state,
stages: [{ slug: selectedStageSlug }],
currentGroup,
featureFlags: {
...state.featureFlags,
},
...mockGetters,
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.OK, { stages: [], events: [] });
});
it(`commits ${types.REQUEST_VALUE_STREAMS} and dispatches receiveValueStreamsSuccess with received data on success`, () => {
return testAction(
actions.fetchValueStreams,
null,
state,
[{ type: types.REQUEST_VALUE_STREAMS }],
[
{
payload: {
events: [],
stages: [],
},
type: 'receiveValueStreamsSuccess',
},
],
);
});
describe('with a failing request', () => {
let mockCommit;
beforeEach(() => {
mockCommit = jest.fn();
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.NOT_FOUND);
});
it(`will commit ${types.RECEIVE_VALUE_STREAMS_ERROR}`, () => {
return actions.fetchValueStreams({ state, getters, commit: mockCommit }).catch(() => {
expect(mockCommit.mock.calls).toEqual([
['REQUEST_VALUE_STREAMS'],
['RECEIVE_VALUE_STREAMS_ERROR', httpStatusCodes.NOT_FOUND],
]);
});
});
it(`throws an error`, () => {
return expect(
actions.fetchValueStreams({ state, getters, commit: mockCommit }),
).rejects.toThrow('Request failed with status code 404');
});
});
describe('receiveValueStreamsSuccess', () => {
it(`with a selectedValueStream in state commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'fetchValueStreamData' and 'fetchStageCountValues'`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
state,
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[{ type: 'fetchValueStreamData' }, { type: 'fetchStageCountValues' }],
);
});
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'setSelectedValueStream' and 'fetchStageCountValues'`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
{
...state,
selectedValueStream: null,
},
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[
{ type: 'setSelectedValueStream', payload: selectedValueStream },
{ type: 'fetchStageCountValues' },
],
);
});
});
});
describe('fetchValueStreamData', () => {
beforeEach(() => {
state = {
...state,
stages: [{ slug: selectedStageSlug }],
currentGroup,
featureFlags: {
...state.featureFlags,
},
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.OK, { stages: [], events: [] });
});
it('dispatches fetchGroupStagesAndEvents, fetchStageMedianValues and durationChart/fetchDurationData', () => {
return testAction(
actions.fetchValueStreamData,
null,
state,
[],
[
{ type: 'fetchGroupStagesAndEvents' },
{ type: 'fetchStageMedianValues' },
{ type: 'durationChart/fetchDurationData' },
],
);
});
});
});
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { OVERVIEW_STAGE_CONFIG } from 'ee/analytics/cycle_analytics/constants';
import * as actions from 'ee/analytics/cycle_analytics/store/actions'; import * as actions from 'ee/analytics/cycle_analytics/store/actions';
import * as getters from 'ee/analytics/cycle_analytics/store/getters'; import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types'; import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { import { createdAfter, createdBefore, currentGroup } from 'jest/cycle_analytics/mock_data';
createdAfter,
createdBefore,
currentGroup,
selectedProjects,
} from 'jest/cycle_analytics/mock_data';
import createFlash from '~/flash'; import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status'; import { allowedStages as stages, valueStreams } from '../mock_data';
import {
allowedStages as stages,
customizableStagesAndEvents,
endpoints,
valueStreams,
} from '../mock_data';
const mockStartEventIdentifier = 'issue_first_mentioned_in_commit';
const mockEndEventIdentifier = 'issue_first_added_to_board';
const mockEvents = {
startEventIdentifier: mockStartEventIdentifier,
endEventIdentifier: mockEndEventIdentifier,
};
const group = { fullPath: 'fake_group_full_path' }; const group = { fullPath: 'fake_group_full_path' };
const milestonesPath = 'fake_milestones_path'; const milestonesPath = 'fake_milestones_path';
const labelsPath = 'fake_labels_path'; const labelsPath = 'fake_labels_path';
const stageData = { events: [] };
const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_FOUND}`);
const flashErrorMessage = 'There was an error while fetching value stream analytics data.'; const flashErrorMessage = 'There was an error while fetching value stream analytics data.';
stages[0].hidden = true; stages[0].hidden = true;
const activeStages = stages.filter(({ hidden }) => !hidden); const activeStages = stages.filter(({ hidden }) => !hidden);
const hiddenStage = stages[0];
const [selectedStage] = activeStages;
const selectedStageSlug = selectedStage.slug;
const [selectedValueStream] = valueStreams; const [selectedValueStream] = valueStreams;
const mockGetters = { const mockGetters = {
...@@ -52,7 +27,6 @@ jest.mock('~/flash'); ...@@ -52,7 +27,6 @@ jest.mock('~/flash');
describe('Value Stream Analytics actions', () => { describe('Value Stream Analytics actions', () => {
let state; let state;
let stateWithOverview = null;
let mock; let mock;
beforeEach(() => { beforeEach(() => {
...@@ -91,67 +65,6 @@ describe('Value Stream Analytics actions', () => { ...@@ -91,67 +65,6 @@ describe('Value Stream Analytics actions', () => {
); );
}); });
describe('setSelectedProjects', () => {
describe('with `overview` stage selected', () => {
beforeEach(() => {
stateWithOverview = { ...state, isOverviewStageSelected: () => true };
});
it('will dispatch the "fetchCycleAnalyticsData" action', () => {
return testAction(
actions.setSelectedProjects,
selectedProjects,
stateWithOverview,
[{ type: types.SET_SELECTED_PROJECTS, payload: selectedProjects }],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with non overview stage selected', () => {
beforeEach(() => {
state = { ...state, selectedStage };
});
it('will dispatch the "fetchStageData" and "fetchCycleAnalyticsData" actions', () => {
return testAction(
actions.setSelectedProjects,
selectedProjects,
state,
[{ type: types.SET_SELECTED_PROJECTS, payload: selectedProjects }],
[
{ type: 'fetchStageData', payload: selectedStage.id },
{ type: 'fetchCycleAnalyticsData' },
],
);
});
});
});
describe('setSelectedStage', () => {
const data = { id: 'someStageId' };
it(`dispatches the ${types.SET_SELECTED_STAGE} and ${types.SET_PAGINATION} actions`, () => {
return testAction(actions.setSelectedStage, data, { ...state, selectedValueStream: {} }, [
{ type: types.SET_SELECTED_STAGE, payload: data },
]);
});
});
describe('setSelectedValueStream', () => {
const vs = { id: 'vs-1', name: 'Value stream 1' };
it('refetches the Value Stream Analytics data', () => {
return testAction(
actions.setSelectedValueStream,
vs,
{ ...state, selectedValueStream: {} },
[{ type: types.SET_SELECTED_VALUE_STREAM, payload: vs }],
[{ type: 'fetchValueStreamData' }],
);
});
});
describe('setPaths', () => { describe('setPaths', () => {
it('dispatches the filters/setEndpoints action with enpoints', () => { it('dispatches the filters/setEndpoints action with enpoints', () => {
return testAction( return testAction(
...@@ -173,117 +86,6 @@ describe('Value Stream Analytics actions', () => { ...@@ -173,117 +86,6 @@ describe('Value Stream Analytics actions', () => {
}); });
}); });
describe('fetchStageData', () => {
const headers = {
'X-Next-Page': 2,
'X-Page': 1,
};
beforeEach(() => {
state = { ...state, currentGroup };
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageData).reply(httpStatusCodes.OK, stageData, headers);
});
it(`commits ${types.RECEIVE_STAGE_DATA_SUCCESS} with received data and headers on success`, () => {
return testAction(
actions.fetchStageData,
selectedStageSlug,
state,
[
{
type: types.RECEIVE_STAGE_DATA_SUCCESS,
payload: stageData,
},
{
type: types.SET_PAGINATION,
payload: { page: headers['X-Page'], hasNextPage: true },
},
],
[{ type: 'requestStageData' }],
);
});
describe('without a next page', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock
.onGet(endpoints.stageData)
.reply(httpStatusCodes.OK, { events: [] }, { ...headers, 'X-Next-Page': null });
});
it('sets hasNextPage to false', () => {
return testAction(
actions.fetchStageData,
selectedStageSlug,
state,
[
{
type: types.RECEIVE_STAGE_DATA_SUCCESS,
payload: { events: [] },
},
{
type: types.SET_PAGINATION,
payload: { page: headers['X-Page'], hasNextPage: false },
},
],
[{ type: 'requestStageData' }],
);
});
});
describe('with a failing request', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageData).replyOnce(httpStatusCodes.NOT_FOUND, { error });
});
it('dispatches receiveStageDataError on error', () => {
return testAction(
actions.fetchStageData,
selectedStage,
state,
[],
[
{
type: 'requestStageData',
},
{
type: 'receiveStageDataError',
payload: error,
},
],
);
});
});
});
describe('receiveStageDataError', () => {
const message = 'fake error';
it(`commits the ${types.RECEIVE_STAGE_DATA_ERROR} mutation`, () => {
return testAction(
actions.receiveStageDataError,
{ message },
state,
[
{
type: types.RECEIVE_STAGE_DATA_ERROR,
payload: message,
},
],
[],
);
});
it('will flash an error message', () => {
actions.receiveStageDataError({ commit: () => {} }, {});
expect(createFlash).toHaveBeenCalledWith({
message: 'There was an error fetching data for the selected stage',
});
});
});
describe('fetchCycleAnalyticsData', () => { describe('fetchCycleAnalyticsData', () => {
function mockFetchCycleAnalyticsAction(overrides = {}) { function mockFetchCycleAnalyticsAction(overrides = {}) {
const mocks = { const mocks = {
...@@ -426,165 +228,6 @@ describe('Value Stream Analytics actions', () => { ...@@ -426,165 +228,6 @@ describe('Value Stream Analytics actions', () => {
}); });
}); });
describe('receiveGroupStagesSuccess', () => {
it(`commits the ${types.RECEIVE_GROUP_STAGES_SUCCESS} mutation'`, () => {
return testAction(
actions.receiveGroupStagesSuccess,
{ ...customizableStagesAndEvents.stages },
state,
[
{
type: types.RECEIVE_GROUP_STAGES_SUCCESS,
payload: { ...customizableStagesAndEvents.stages },
},
],
[],
);
});
});
describe('setDefaultSelectedStage', () => {
it("dispatches the 'setSelectedStage' with the overview stage", () => {
return testAction(
actions.setDefaultSelectedStage,
null,
state,
[],
[{ type: 'setSelectedStage', payload: OVERVIEW_STAGE_CONFIG }],
);
});
});
describe('fetchStageMedianValues', () => {
let mockDispatch = jest.fn();
const fetchMedianResponse = activeStages.map(({ slug: id }) => ({ events: [], id }));
beforeEach(() => {
state = { ...state, stages, currentGroup };
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.OK, { events: [] });
mockDispatch = jest.fn();
});
it('dispatches receiveStageMedianValuesSuccess with received data on success', () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[{ type: types.RECEIVE_STAGE_MEDIANS_SUCCESS, payload: fetchMedianResponse }],
[{ type: 'requestStageMedianValues' }],
);
});
it('does not request hidden stages', () => {
return actions
.fetchStageMedianValues({
state,
getters: {
...getters,
activeStages,
},
commit: () => {},
dispatch: mockDispatch,
})
.then(() => {
expect(mockDispatch).not.toHaveBeenCalledWith('receiveStageMedianValuesSuccess', {
events: [],
id: hiddenStage.id,
});
});
});
describe(`Status ${httpStatusCodes.OK} and error message in response`, () => {
const dataError = 'Too much data';
const payload = activeStages.map(({ slug: id }) => ({ value: null, id, error: dataError }));
beforeEach(() => {
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.OK, { error: dataError });
});
it(`dispatches the 'RECEIVE_STAGE_MEDIANS_SUCCESS' with ${dataError}`, () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[{ type: types.RECEIVE_STAGE_MEDIANS_SUCCESS, payload }],
[{ type: 'requestStageMedianValues' }],
);
});
});
describe('with a failing request', () => {
beforeEach(() => {
mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.NOT_FOUND, { error });
});
it('will dispatch receiveStageMedianValuesError', () => {
return testAction(
actions.fetchStageMedianValues,
null,
state,
[],
[
{ type: 'requestStageMedianValues' },
{ type: 'receiveStageMedianValuesError', payload: error },
],
);
});
});
});
describe('receiveStageMedianValuesError', () => {
it(`commits the ${types.RECEIVE_STAGE_MEDIANS_ERROR} mutation`, () =>
testAction(
actions.receiveStageMedianValuesError,
{},
state,
[
{
type: types.RECEIVE_STAGE_MEDIANS_ERROR,
payload: {},
},
],
[],
));
it('will flash an error message', () => {
actions.receiveStageMedianValuesError({ commit: () => {} });
expect(createFlash).toHaveBeenCalledWith({
message: 'There was an error fetching median data for stages',
});
});
});
describe('fetchStageCountValues', () => {
const fetchCountResponse = activeStages.map(({ slug: id }) => ({ events: [], id }));
beforeEach(() => {
state = {
...state,
stages,
currentGroup,
featureFlags: state.featureFlags,
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageCount).reply(httpStatusCodes.OK, { events: [] });
});
it('dispatches receiveStageCountValuesSuccess with received data on success', () => {
return testAction(
actions.fetchStageCountValues,
null,
state,
[
{ type: types.REQUEST_STAGE_COUNTS },
{ type: types.RECEIVE_STAGE_COUNTS_SUCCESS, payload: fetchCountResponse },
],
[],
);
});
});
describe('initializeCycleAnalytics', () => { describe('initializeCycleAnalytics', () => {
let mockDispatch; let mockDispatch;
let mockCommit; let mockCommit;
...@@ -697,333 +340,4 @@ describe('Value Stream Analytics actions', () => { ...@@ -697,333 +340,4 @@ describe('Value Stream Analytics actions', () => {
[], [],
)); ));
}); });
describe('createValueStream', () => {
const payload = {
name: 'cool value stream',
stages: [
{
...selectedStage,
...mockEvents,
id: null,
},
{ ...hiddenStage, ...mockEvents },
],
};
const createResp = { id: 'new value stream', is_custom: true, ...payload };
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onPost(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, createResp);
});
it(`commits the ${types.REQUEST_CREATE_VALUE_STREAM} and ${types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.createValueStream,
payload,
state,
[
{
type: types.REQUEST_CREATE_VALUE_STREAM,
},
],
[{ type: 'receiveCreateValueStreamSuccess', payload: createResp }],
);
});
});
describe('with errors', () => {
const errors = { name: ['is taken'] };
const message = { message: 'error' };
const resp = { message, payload: { errors } };
beforeEach(() => {
mock.onPost(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_CREATE_VALUE_STREAM} and ${types.RECEIVE_CREATE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(
actions.createValueStream,
payload,
state,
[
{ type: types.REQUEST_CREATE_VALUE_STREAM },
{
type: types.RECEIVE_CREATE_VALUE_STREAM_ERROR,
payload: { message, data: payload, errors },
},
],
[],
);
});
});
});
describe('updateValueStream', () => {
const payload = {
name: 'cool value stream',
stages: [
{
...selectedStage,
...mockEvents,
id: 'stage-1',
},
{ ...hiddenStage, ...mockEvents },
],
};
const updateResp = { id: 'new value stream', is_custom: true, ...payload };
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onPut(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, updateResp);
});
it(`commits the ${types.REQUEST_UPDATE_VALUE_STREAM} and ${types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.updateValueStream,
payload,
state,
[
{ type: types.REQUEST_UPDATE_VALUE_STREAM },
{ type: types.RECEIVE_UPDATE_VALUE_STREAM_SUCCESS, payload: updateResp },
],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with errors', () => {
const errors = { name: ['is taken'] };
const message = { message: 'error' };
const resp = { message, payload: { errors } };
beforeEach(() => {
mock.onPut(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_UPDATE_VALUE_STREAM} and ${types.RECEIVE_UPDATE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(actions.updateValueStream, payload, state, [
{ type: types.REQUEST_UPDATE_VALUE_STREAM },
{
type: types.RECEIVE_UPDATE_VALUE_STREAM_ERROR,
payload: { message, data: payload, errors },
},
]);
});
});
});
describe('deleteValueStream', () => {
const payload = 'my-fake-value-stream';
beforeEach(() => {
state = { currentGroup };
});
describe('with no errors', () => {
beforeEach(() => {
mock.onDelete(endpoints.valueStreamData).replyOnce(httpStatusCodes.OK, {});
});
it(`commits the ${types.REQUEST_DELETE_VALUE_STREAM} and ${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} actions`, () => {
return testAction(
actions.deleteValueStream,
payload,
state,
[
{
type: types.REQUEST_DELETE_VALUE_STREAM,
},
{
type: types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS,
},
],
[{ type: 'fetchCycleAnalyticsData' }],
);
});
});
describe('with errors', () => {
const message = { message: 'failed to delete the value stream' };
const resp = { message };
beforeEach(() => {
mock.onDelete(endpoints.valueStreamData).replyOnce(httpStatusCodes.NOT_FOUND, resp);
});
it(`commits the ${types.REQUEST_DELETE_VALUE_STREAM} and ${types.RECEIVE_DELETE_VALUE_STREAM_ERROR} actions `, () => {
return testAction(
actions.deleteValueStream,
payload,
state,
[
{ type: types.REQUEST_DELETE_VALUE_STREAM },
{
type: types.RECEIVE_DELETE_VALUE_STREAM_ERROR,
payload: message,
},
],
[],
);
});
});
});
describe('fetchValueStreams', () => {
beforeEach(() => {
state = {
...state,
stages: [{ slug: selectedStageSlug }],
currentGroup,
featureFlags: {
...state.featureFlags,
},
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.OK, { stages: [], events: [] });
});
it(`commits ${types.REQUEST_VALUE_STREAMS} and dispatches receiveValueStreamsSuccess with received data on success`, () => {
return testAction(
actions.fetchValueStreams,
null,
state,
[{ type: types.REQUEST_VALUE_STREAMS }],
[
{
payload: {
events: [],
stages: [],
},
type: 'receiveValueStreamsSuccess',
},
],
);
});
describe('with a failing request', () => {
let mockCommit;
beforeEach(() => {
mockCommit = jest.fn();
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.NOT_FOUND);
});
it(`will commit ${types.RECEIVE_VALUE_STREAMS_ERROR}`, () => {
return actions.fetchValueStreams({ state, getters, commit: mockCommit }).catch(() => {
expect(mockCommit.mock.calls).toEqual([
['REQUEST_VALUE_STREAMS'],
['RECEIVE_VALUE_STREAMS_ERROR', httpStatusCodes.NOT_FOUND],
]);
});
});
it(`throws an error`, () => {
return expect(
actions.fetchValueStreams({ state, getters, commit: mockCommit }),
).rejects.toThrow('Request failed with status code 404');
});
});
describe('receiveValueStreamsSuccess', () => {
it(`with a selectedValueStream in state commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'fetchValueStreamData' and 'fetchStageCountValues'`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
state,
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[{ type: 'fetchValueStreamData' }, { type: 'fetchStageCountValues' }],
);
});
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'setSelectedValueStream' and 'fetchStageCountValues'`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
{
...state,
selectedValueStream: null,
},
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[
{ type: 'setSelectedValueStream', payload: selectedValueStream },
{ type: 'fetchStageCountValues' },
],
);
});
});
});
describe('fetchValueStreamData', () => {
beforeEach(() => {
state = {
...state,
stages: [{ slug: selectedStageSlug }],
currentGroup,
featureFlags: {
...state.featureFlags,
},
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.OK, { stages: [], events: [] });
});
it('dispatches fetchGroupStagesAndEvents, fetchStageMedianValues and durationChart/fetchDurationData', () => {
return testAction(
actions.fetchValueStreamData,
null,
state,
[],
[
{ type: 'fetchGroupStagesAndEvents' },
{ type: 'fetchStageMedianValues' },
{ type: 'durationChart/fetchDurationData' },
],
);
});
});
describe.each`
targetAction | payload | mutations
${actions.setDateRange} | ${{ createdAfter, createdBefore }} | ${[{ type: 'SET_DATE_RANGE', payload: { createdAfter, createdBefore } }]}
${actions.setFilters} | ${''} | ${[]}
`('$action', ({ targetAction, payload, mutations }) => {
beforeEach(() => {
stateWithOverview = { ...state, isOverviewStageSelected: () => true };
});
it('dispatches the fetchCycleAnalyticsData action', () => {
return testAction(targetAction, payload, stateWithOverview, mutations, [
{ type: 'fetchCycleAnalyticsData' },
]);
});
describe('with a stage selected', () => {
beforeEach(() => {
stateWithOverview = { ...state, selectedStage };
});
it('dispatches the fetchStageData action', () => {
return testAction(targetAction, payload, stateWithOverview, mutations, [
{ type: 'fetchStageData', payload: selectedStage.id },
{ type: 'fetchCycleAnalyticsData' },
]);
});
});
});
}); });
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