Commit b5afaac5 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Map type of work vuex to module

Replaces the state / getters
in the base.vue with their respective
code from the tasks by type module

Update vuex and base specs

Fix utils_spec test
parent 2ddaad86
......@@ -52,8 +52,6 @@ export default {
'featureFlags',
'isLoading',
'isLoadingStage',
'isLoadingTasksByTypeChart',
'isLoadingTasksByTypeChartTopLabels',
'isEmptyStage',
'isSavingCustomStage',
'isCreatingCustomStage',
......@@ -63,25 +61,30 @@ export default {
'selectedStage',
'stages',
'summary',
'topRankedLabels',
'currentStageEvents',
'customStageFormEvents',
'errorCode',
'startDate',
'endDate',
'tasksByType',
'medians',
'customStageFormErrors',
]),
...mapState('typeOfWork', [
'isLoadingTasksByTypeChart',
'isLoadingTasksByTypeChartTopLabels',
'topRankedLabels',
'subject',
'selectedLabelIds',
]),
...mapGetters([
'hasNoAccessError',
'currentGroupPath',
'tasksByTypeChartData',
'activeStages',
'selectedProjectIds',
'enableCustomOrdering',
'cycleAnalyticsRequestParams',
]),
...mapGetters('typeOfWork', ['tasksByTypeChartData']),
shouldRenderEmptyState() {
return !this.selectedGroup;
},
......@@ -95,12 +98,10 @@ export default {
return this.featureFlags.hasDurationChart && !this.hasNoAccessError && !this.isLoading;
},
shouldDisplayTypeOfWorkCharts() {
return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError;
return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError && !this.isLoading;
},
isLoadingTypeOfWork() {
return (
this.isLoading || this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart
);
return this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart;
},
hasDateRangeSet() {
return this.startDate && this.endDate;
......@@ -111,7 +112,8 @@ export default {
startDate,
endDate,
selectedProjectIds,
tasksByType: { subject, selectedLabelIds },
subject,
selectedLabelIds,
} = this;
return {
selectedGroup,
......@@ -152,16 +154,15 @@ export default {
'showCustomStageForm',
'showEditCustomStageForm',
'setDateRange',
'fetchTasksByTypeData',
'createCustomStage',
'updateStage',
'removeStage',
'setFeatureFlags',
'clearCustomStageFormErrors',
'updateStage',
'setTasksByTypeFilters',
'reorderStage',
]),
...mapActions('typeOfWork', ['setTasksByTypeFilters']),
onGroupSelect(group) {
this.setSelectedGroup(group);
this.fetchCycleAnalyticsData();
......
......@@ -3,14 +3,7 @@ import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types';
import { removeFlash } from '../utils';
const handleErrorOrRethrow = ({ action, error }) => {
if (error?.response?.status === httpStatus.FORBIDDEN) {
throw error;
}
action();
};
import { removeFlash, handleErrorOrRethrow } from '../utils';
const isStageNameExistsError = ({ status, errors }) => {
const ERROR_NAME_RESERVED = 'is reserved';
......@@ -105,8 +98,10 @@ export const fetchStageMedianValues = ({ state, dispatch, getters }) => {
};
export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_CYCLE_ANALYTICS_DATA);
export const receiveCycleAnalyticsDataSuccess = ({ commit }) =>
export const receiveCycleAnalyticsDataSuccess = ({ commit, dispatch }) => {
commit(types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS);
dispatch('typeOfWork/fetchTopRankedGroupLabels');
};
export const receiveCycleAnalyticsDataError = ({ commit }, { response }) => {
const { status } = response;
......
......@@ -2,7 +2,6 @@ import dateFormat from 'dateformat';
import { isNumber } from 'lodash';
import httpStatus from '~/lib/utils/http_status';
import { dateFormats } from '../../shared/constants';
import { getTasksByTypeData } from '../utils';
export const hasNoAccessError = state => state.errorCode === httpStatus.FORBIDDEN;
......
import Api from 'ee/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types';
const handleErrorOrRethrow = ({ action, error }) => {
if (error?.response?.status === httpStatus.FORBIDDEN) {
throw error;
}
action();
};
import { handleErrorOrRethrow } from '../../../utils';
export const receiveTopRankedGroupLabelsSuccess = ({ commit, dispatch }, data) => {
commit(types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS, data);
......@@ -21,19 +14,13 @@ export const receiveTopRankedGroupLabelsError = ({ commit }, error) => {
createFlash(__('There was an error fetching the top labels for the selected group'));
};
export const requestTopRankedGroupLabels = ({ commit }) =>
export const fetchTopRankedGroupLabels = ({ dispatch, commit, state, rootGetters }) => {
commit(types.REQUEST_TOP_RANKED_GROUP_LABELS);
export const fetchTopRankedGroupLabels = ({
dispatch,
state,
getters: {
const {
currentGroupPath,
cycleAnalyticsRequestParams: { created_after, created_before },
},
}) => {
dispatch('requestTopRankedGroupLabels');
const { subject } = state.tasksByType;
} = rootGetters;
const { subject } = state;
return Api.cycleAnalyticsTopLabels(currentGroupPath, {
subject,
......@@ -49,29 +36,21 @@ export const fetchTopRankedGroupLabels = ({
);
};
export const receiveTasksByTypeDataSuccess = ({ commit }, data) => {
commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, data);
};
export const receiveTasksByTypeDataError = ({ commit }, error) => {
commit(types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR, error);
createFlash(__('There was an error fetching data for the tasks by type chart'));
};
export const requestTasksByTypeData = ({ commit }) => commit(types.REQUEST_TASKS_BY_TYPE_DATA);
export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
export const fetchTasksByTypeData = ({ dispatch, commit, state, rootGetters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams: { created_after, created_before, project_ids },
} = getters;
} = rootGetters;
const {
tasksByType: { subject, selectedLabelIds },
} = state;
const { subject, selectedLabelIds } = state;
// ensure we clear any chart data currently in state
dispatch('requestTasksByTypeData');
commit(types.REQUEST_TASKS_BY_TYPE_DATA);
// dont request if we have no labels selected...for now
if (selectedLabelIds.length) {
......@@ -84,27 +63,13 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
};
return Api.cycleAnalyticsTasksByType(currentGroupPath, params)
.then(({ data }) => dispatch('receiveTasksByTypeDataSuccess', data))
.then(({ data }) => commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, data))
.catch(error => dispatch('receiveTasksByTypeDataError', error));
}
return dispatch('receiveTasksByTypeDataSuccess', []);
return commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, []);
};
export const setTasksByTypeFilters = ({ dispatch, commit }, data) => {
commit(types.SET_TASKS_BY_TYPE_FILTERS, data);
dispatch('fetchTopRankedGroupLabels');
};
export const initializeCycleAnalyticsSuccess = ({ commit }) =>
commit(types.INITIALIZE_CYCLE_ANALYTICS_SUCCESS);
export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {}) => {
commit(types.INITIALIZE_CYCLE_ANALYTICS, initialData);
if (initialData?.group?.fullPath) {
return dispatch('fetchCycleAnalyticsData').then(() =>
dispatch('initializeCycleAnalyticsSuccess'),
);
}
return dispatch('initializeCycleAnalyticsSuccess');
dispatch('fetchTasksByTypeData');
};
import { getTasksByTypeData } from '../../../utils';
export const tasksByTypeChartData = ({ tasksByType, startDate, endDate }) => {
if (tasksByType && tasksByType.data.length) {
return getTasksByTypeData({
data: tasksByType.data,
export const tasksByTypeChartData = ({ data = [] } = {}, _, rootState = {}) => {
const { startDate = null, endDate = null } = rootState;
return data.length
? getTasksByTypeData({
data,
startDate,
endDate,
});
}
return { groupBy: [], data: [], seriesNames: [] };
})
: { groupBy: [], data: [], seriesNames: [] };
};
export default { tasksByTypeChartData };
export default () => ({ tasksByTypeChartData });
......@@ -2,11 +2,10 @@ export const REQUEST_TOP_RANKED_GROUP_LABELS = 'REQUEST_TOP_RANKED_GROUP_LABELS'
export const RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS = 'RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS';
export const RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR = 'RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR';
export const SET_TASKS_BY_TYPE_SUBJECT = 'SET_TASKS_BY_TYPE_SUBJECT';
export const SET_TASKS_BY_TYPE_LABELS = 'SET_TASKS_BY_TYPE_LABELS';
export const REQUEST_TASKS_BY_TYPE_DATA = 'REQUEST_TASKS_BY_TYPE_DATA';
export const RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS = 'RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS';
export const RECEIVE_TASKS_BY_TYPE_DATA_ERROR = 'RECEIVE_TASKS_BY_TYPE_DATA_ERROR';
export const SET_TASKS_BY_TYPE_SUBJECT = 'SET_TASKS_BY_TYPE_SUBJECT';
export const SET_TASKS_BY_TYPE_LABELS = 'SET_TASKS_BY_TYPE_LABELS';
export const SET_TASKS_BY_TYPE_FILTERS = 'SET_TASKS_BY_TYPE_FILTERS';
......@@ -7,28 +7,17 @@ export default {
[types.REQUEST_TOP_RANKED_GROUP_LABELS](state) {
state.isLoadingTasksByTypeChartTopLabels = true;
state.topRankedLabels = [];
state.tasksByType = {
...state.tasksByType,
selectedLabelIds: [],
};
state.selectedLabelIds = [];
},
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS](state, data = []) {
const { tasksByType } = state;
state.isLoadingTasksByTypeChartTopLabels = false;
state.topRankedLabels = data.map(convertObjectPropsToCamelCase);
state.tasksByType = {
...tasksByType,
selectedLabelIds: data.map(({ id }) => id),
};
state.selectedLabelIds = data.map(({ id }) => id);
},
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR](state) {
const { tasksByType } = state;
state.isLoadingTasksByTypeChartTopLabels = false;
state.topRankedLabels = [];
state.tasksByType = {
...tasksByType,
selectedLabelIds: [],
};
state.selectedLabelIds = [];
},
[types.REQUEST_TASKS_BY_TYPE_DATA](state) {
state.isLoadingTasksByTypeChart = true;
......@@ -38,28 +27,19 @@ export default {
},
[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, data = []) {
state.isLoadingTasksByTypeChart = false;
state.tasksByType = {
...state.tasksByType,
data: transformRawTasksByTypeData(data),
};
state.data = transformRawTasksByTypeData(data);
},
[types.SET_TASKS_BY_TYPE_FILTERS](state, { filter, value }) {
const {
tasksByType: { selectedLabelIds, ...tasksByTypeRest },
} = state;
let updatedFilter = {};
const { selectedLabelIds } = state;
switch (filter) {
case TASKS_BY_TYPE_FILTERS.LABEL:
updatedFilter = {
selectedLabelIds: toggleSelectedLabel({ selectedLabelIds, value }),
};
state.selectedLabelIds = toggleSelectedLabel({ selectedLabelIds, value });
break;
case TASKS_BY_TYPE_FILTERS.SUBJECT:
updatedFilter = { subject: value };
state.subject = value;
break;
default:
break;
}
state.tasksByType = { ...tasksByTypeRest, selectedLabelIds, ...updatedFilter };
},
};
......@@ -5,7 +5,7 @@ export default () => ({
isLoadingTasksByTypeChartTopLabels: false,
subject: TASKS_BY_TYPE_SUBJECT_ISSUE,
topRankedLabels: [],
selectedLabelIds: [],
topRankedLabels: [],
data: [],
});
import { isNumber } from 'lodash';
import dateFormat from 'dateformat';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import httpStatus from '~/lib/utils/http_status';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
import { hideFlash } from '~/flash';
import {
......@@ -329,3 +330,10 @@ export const getTasksByTypeData = ({ data = [], startDate = null, endDate = null
groupBy,
};
};
export const handleErrorOrRethrow = ({ action, error }) => {
if (error?.response?.status === httpStatus.FORBIDDEN) {
throw error;
}
action();
};
import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
import Vuex from 'vuex';
import Vue from 'vue';
import store from 'ee/analytics/cycle_analytics/store';
import Component from 'ee/analytics/cycle_analytics/components/base.vue';
import { GlEmptyState } from '@gitlab/ui';
......@@ -291,16 +290,15 @@ describe('Cycle Analytics component', () => {
expect(second.classes('active')).toBe(false);
});
it('can navigate to different stages', done => {
it('can navigate to different stages', () => {
selectStageNavItem(2).trigger('click');
Vue.nextTick(() => {
return wrapper.vm.$nextTick().then(() => {
const first = selectStageNavItem(0);
const third = selectStageNavItem(2);
expect(third.classes('active')).toBe(true);
expect(first.classes('active')).toBe(false);
done();
});
});
});
......
......@@ -10,7 +10,10 @@ import {
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getDateInPast, getDatesInRange } from '~/lib/utils/datetime_utility';
import { toYmd } from 'ee/analytics/shared/utils';
import { transformRawTasksByTypeData } from 'ee/analytics/cycle_analytics/utils';
import {
getTasksByTypeData,
transformRawTasksByTypeData,
} from 'ee/analytics/cycle_analytics/utils';
const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents: 'analytics/value_stream_analytics/stages.json', // customizable stages and events endpoint
......@@ -145,7 +148,7 @@ export const customStageFormErrors = convertObjectPropsToCamelCase(rawCustomStag
const dateRange = getDatesInRange(startDate, endDate, toYmd);
export const rawTasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_type.json').map(
export const apiTasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_type.json').map(
labelData => {
// add data points for our mock date range
const maxValue = 10;
......@@ -157,7 +160,8 @@ export const rawTasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_b
},
);
export const transformedTasksByTypeData = transformRawTasksByTypeData(rawTasksByTypeData);
export const rawTasksByTypeData = transformRawTasksByTypeData(apiTasksByTypeData);
export const transformedTasksByTypeData = getTasksByTypeData(apiTasksByTypeData);
export const tasksByTypeData = {
seriesNames: ['Cool label', 'Normal label'],
......
......@@ -4,16 +4,11 @@ import testAction from 'helpers/vuex_action_helper';
import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import * as actions from 'ee/analytics/cycle_analytics/store/actions';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import {
TASKS_BY_TYPE_FILTERS,
TASKS_BY_TYPE_SUBJECT_ISSUE,
} from 'ee/analytics/cycle_analytics/constants';
import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import {
selectedGroup,
allowedStages as stages,
groupLabels,
startDate,
endDate,
customizableStagesAndEvents,
......@@ -180,87 +175,6 @@ describe('Cycle analytics actions', () => {
});
});
describe('fetchTopRankedGroupLabels', () => {
beforeEach(() => {
gon.api_version = 'v4';
state = { selectedGroup, tasksByType: { subject: TASKS_BY_TYPE_SUBJECT_ISSUE }, ...getters };
});
describe('succeeds', () => {
beforeEach(() => {
mock.onGet(endpoints.tasksByTypeTopLabelsData).replyOnce(200, groupLabels);
});
it('dispatches receiveTopRankedGroupLabelsSuccess if the request succeeds', () => {
return testAction(
actions.fetchTopRankedGroupLabels,
null,
state,
[],
[
{ type: 'requestTopRankedGroupLabels' },
{ type: 'receiveTopRankedGroupLabelsSuccess', payload: groupLabels },
],
);
});
describe('receiveTopRankedGroupLabelsSuccess', () => {
beforeEach(() => {
setFixtures('<div class="flash-container"></div>');
});
it(`commits the ${types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS} mutation and dispatches the 'fetchTasksByTypeData' action`, done => {
testAction(
actions.receiveTopRankedGroupLabelsSuccess,
null,
state,
[
{
type: types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS,
payload: null,
},
],
[{ type: 'fetchTasksByTypeData' }],
done,
);
});
});
});
describe('with an error', () => {
beforeEach(() => {
mock.onGet(endpoints.fetchTopRankedGroupLabels).replyOnce(404);
});
it('dispatches receiveTopRankedGroupLabelsError if the request fails', () => {
return testAction(
actions.fetchTopRankedGroupLabels,
null,
state,
[],
[
{ type: 'requestTopRankedGroupLabels' },
{ type: 'receiveTopRankedGroupLabelsError', payload: error },
],
);
});
});
describe('receiveTopRankedGroupLabelsError', () => {
beforeEach(() => {
setFixtures('<div class="flash-container"></div>');
});
it('flashes an error message if the request fails', () => {
actions.receiveTopRankedGroupLabelsError({
commit: () => {},
});
shouldFlashAMessage('There was an error fetching the top labels for the selected group');
});
});
});
describe('fetchCycleAnalyticsData', () => {
function mockFetchCycleAnalyticsAction(overrides = {}) {
const mocks = {
......@@ -845,31 +759,6 @@ describe('Cycle analytics actions', () => {
});
});
describe('setTasksByTypeFilters', () => {
const filter = TASKS_BY_TYPE_FILTERS.SUBJECT;
const value = 'issue';
it(`commits the ${types.SET_TASKS_BY_TYPE_FILTERS} mutation and dispatches 'fetchTopRankedGroupLabels'`, done => {
testAction(
actions.setTasksByTypeFilters,
{ filter, value },
{},
[
{
type: types.SET_TASKS_BY_TYPE_FILTERS,
payload: { filter, value },
},
],
[
{
type: 'fetchTopRankedGroupLabels',
},
],
done,
);
});
});
describe('createCustomStage', () => {
describe('with valid data', () => {
const customStageData = {
......
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as rootGetters from 'ee/analytics/cycle_analytics/store/getters';
import * as getters from 'ee/analytics/cycle_analytics/store/modules/type_of_work/getters';
import * as actions from 'ee/analytics/cycle_analytics/store/modules/type_of_work/actions';
import * as types from 'ee/analytics/cycle_analytics/store/modules/type_of_work/mutation_types';
import {
TASKS_BY_TYPE_FILTERS,
TASKS_BY_TYPE_SUBJECT_ISSUE,
} from 'ee/analytics/cycle_analytics/constants';
import httpStatusCodes from '~/lib/utils/http_status';
import { groupLabels, endpoints, startDate, endDate } from '../../../mock_data';
import { shouldFlashAMessage } from '../../../helpers';
const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_FOUND}`);
describe('Type of work actions', () => {
let mock;
let state = {
isLoadingTasksByTypeChart: false,
isLoadingTasksByTypeChartTopLabels: false,
subject: TASKS_BY_TYPE_SUBJECT_ISSUE,
topRankedLabels: [],
selectedLabelIds: [],
data: [],
};
const mockedState = {
...rootGetters,
...getters,
...state,
rootState: { startDate, endDate },
};
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
state = { ...mockedState, selectedGroup: null };
});
describe('fetchTopRankedGroupLabels', () => {
beforeEach(() => {
gon.api_version = 'v4';
state = { ...mockedState, subject: TASKS_BY_TYPE_SUBJECT_ISSUE };
});
describe('succeeds', () => {
beforeEach(() => {
mock.onGet(endpoints.tasksByTypeTopLabelsData).replyOnce(200, groupLabels);
});
it('dispatches receiveTopRankedGroupLabelsSuccess if the request succeeds', () => {
return testAction(
actions.fetchTopRankedGroupLabels,
null,
state,
[{ type: 'REQUEST_TOP_RANKED_GROUP_LABELS' }],
[{ type: 'receiveTopRankedGroupLabelsSuccess', payload: groupLabels }],
);
});
describe('receiveTopRankedGroupLabelsSuccess', () => {
beforeEach(() => {
setFixtures('<div class="flash-container"></div>');
});
it(`commits the ${types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS} mutation and dispatches the 'fetchTasksByTypeData' action`, () => {
return testAction(
actions.receiveTopRankedGroupLabelsSuccess,
null,
state,
[
{
type: types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS,
payload: null,
},
],
[{ type: 'fetchTasksByTypeData' }],
);
});
});
});
describe('with an error', () => {
beforeEach(() => {
mock.onGet(endpoints.fetchTopRankedGroupLabels).replyOnce(404);
});
it('dispatches receiveTopRankedGroupLabelsError if the request fails', () => {
return testAction(
actions.fetchTopRankedGroupLabels,
null,
state,
[{ type: 'REQUEST_TOP_RANKED_GROUP_LABELS' }],
[{ type: 'receiveTopRankedGroupLabelsError', payload: error }],
);
});
});
describe('receiveTopRankedGroupLabelsError', () => {
beforeEach(() => {
setFixtures('<div class="flash-container"></div>');
});
it('flashes an error message if the request fails', () => {
actions.receiveTopRankedGroupLabelsError({
commit: () => {},
});
shouldFlashAMessage('There was an error fetching the top labels for the selected group');
});
});
});
describe('setTasksByTypeFilters', () => {
const filter = TASKS_BY_TYPE_FILTERS.SUBJECT;
const value = 'issue';
it(`commits the ${types.SET_TASKS_BY_TYPE_FILTERS} mutation and dispatches 'fetchTasksByTypeData'`, () => {
return testAction(
actions.setTasksByTypeFilters,
{ filter, value },
{},
[
{
type: types.SET_TASKS_BY_TYPE_FILTERS,
payload: { filter, value },
},
],
[
{
type: 'fetchTasksByTypeData',
},
],
);
});
});
});
import { tasksByTypeChartData } from 'ee/analytics/cycle_analytics/store/modules/type_of_work/getters';
import {
rawTasksByTypeData,
transformedTasksByTypeData,
startDate,
endDate,
} from '../../../mock_data';
describe('Type of work getters', () => {
describe('tasksByTypeChartData', () => {
const rootState = { startDate, endDate };
describe('with data', () => {
it('correctly transforms the raw task by type data', () => {
expect(tasksByTypeChartData(rawTasksByTypeData, null, rootState)).toEqual(
transformedTasksByTypeData,
);
});
});
describe('with no data', () => {
it('returns all required properties', () => {
expect(tasksByTypeChartData()).toEqual({ groupBy: [], data: [], seriesNames: [] });
});
});
});
});
import mutations from 'ee/analytics/cycle_analytics/store/modules/type_of_work/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/modules/type_of_work/mutation_types';
import { TASKS_BY_TYPE_FILTERS } from 'ee/analytics/cycle_analytics/constants';
import { apiTasksByTypeData, rawTasksByTypeData } from '../../../mock_data';
let state = null;
describe('Cycle analytics mutations', () => {
beforeEach(() => {
state = {};
});
afterEach(() => {
state = null;
});
it.each`
mutation | stateKey | value
${types.REQUEST_TOP_RANKED_GROUP_LABELS} | ${'topRankedLabels'} | ${[]}
${types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR} | ${'topRankedLabels'} | ${[]}
${types.REQUEST_TASKS_BY_TYPE_DATA} | ${'isLoadingTasksByTypeChart'} | ${true}
${types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR} | ${'isLoadingTasksByTypeChart'} | ${false}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
mutations[mutation](state);
expect(state[stateKey]).toEqual(value);
});
describe(`${types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS}`, () => {
it('sets isLoadingTasksByTypeChart to false', () => {
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, {});
expect(state.isLoadingTasksByTypeChart).toEqual(false);
});
it('sets data to the raw returned chart data', () => {
state = { data: null };
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, apiTasksByTypeData);
expect(state.data).toEqual(rawTasksByTypeData);
});
});
describe(`${types.SET_TASKS_BY_TYPE_FILTERS}`, () => {
it('will update the tasksByType state key', () => {
state = {};
const subjectFilter = { filter: TASKS_BY_TYPE_FILTERS.SUBJECT, value: 'cool-subject' };
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, subjectFilter);
expect(state.subject).toEqual('cool-subject');
});
it('will toggle the specified label id in the selectedLabelIds state key', () => {
state = {
selectedLabelIds: [10, 20, 30],
};
const labelFilter = { filter: TASKS_BY_TYPE_FILTERS.LABEL, value: 20 };
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, labelFilter);
expect(state.selectedLabelIds).toEqual([10, 30]);
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, labelFilter);
expect(state.selectedLabelIds).toEqual([10, 30, 20]);
});
});
});
import mutations from 'ee/analytics/cycle_analytics/store/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import { TASKS_BY_TYPE_FILTERS } from 'ee/analytics/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
......@@ -15,8 +14,6 @@ import {
startDate,
endDate,
customizableStagesAndEvents,
rawTasksByTypeData,
transformedTasksByTypeData,
selectedProjects,
} from '../mock_data';
......@@ -47,8 +44,6 @@ describe('Cycle analytics mutations', () => {
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true}
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false}
${types.REQUEST_CYCLE_ANALYTICS_DATA} | ${'isLoading'} | ${true}
${types.REQUEST_TOP_RANKED_GROUP_LABELS} | ${'topRankedLabels'} | ${[]}
${types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR} | ${'topRankedLabels'} | ${[]}
${types.RECEIVE_GROUP_STAGES_AND_EVENTS_ERROR} | ${'stages'} | ${[]}
${types.REQUEST_GROUP_STAGES_AND_EVENTS} | ${'stages'} | ${[]}
${types.RECEIVE_GROUP_STAGES_AND_EVENTS_ERROR} | ${'customStageFormEvents'} | ${[]}
......@@ -57,8 +52,6 @@ describe('Cycle analytics mutations', () => {
${types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS} | ${'isSavingCustomStage'} | ${false}
${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'customStageFormErrors'} | ${{}}
${types.REQUEST_TASKS_BY_TYPE_DATA} | ${'isLoadingTasksByTypeChart'} | ${true}
${types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR} | ${'isLoadingTasksByTypeChart'} | ${false}
${types.REQUEST_UPDATE_STAGE} | ${'isLoading'} | ${true}
${types.REQUEST_UPDATE_STAGE} | ${'isSavingCustomStage'} | ${true}
${types.REQUEST_UPDATE_STAGE} | ${'customStageFormErrors'} | ${null}
......@@ -176,21 +169,6 @@ describe('Cycle analytics mutations', () => {
});
});
describe(`${types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS}`, () => {
it('sets isLoadingTasksByTypeChart to false', () => {
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, {});
expect(state.isLoadingTasksByTypeChart).toEqual(false);
});
it('sets tasksByType.data to the raw returned chart data', () => {
state = { tasksByType: { data: null } };
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, rawTasksByTypeData);
expect(state.tasksByType.data).toEqual(transformedTasksByTypeData);
});
});
describe(`${types.RECEIVE_STAGE_MEDIANS_SUCCESS}`, () => {
it('sets each id as a key in the median object with the corresponding value', () => {
const stateWithData = {
......@@ -206,29 +184,6 @@ describe('Cycle analytics mutations', () => {
});
});
describe(`${types.SET_TASKS_BY_TYPE_FILTERS}`, () => {
it('will update the tasksByType state key', () => {
state = { tasksByType: {} };
const subjectFilter = { filter: TASKS_BY_TYPE_FILTERS.SUBJECT, value: 'cool-subject' };
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, subjectFilter);
expect(state.tasksByType).toEqual({ subject: 'cool-subject' });
});
it('will toggle the specified label id in the tasksByType.selectedLabelIds state key', () => {
state = {
tasksByType: { selectedLabelIds: [10, 20, 30] },
};
const labelFilter = { filter: TASKS_BY_TYPE_FILTERS.LABEL, value: 20 };
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, labelFilter);
expect(state.tasksByType).toEqual({ selectedLabelIds: [10, 30] });
mutations[types.SET_TASKS_BY_TYPE_FILTERS](state, labelFilter);
expect(state.tasksByType).toEqual({ selectedLabelIds: [10, 30, 20] });
});
});
describe(`${types.INITIALIZE_CYCLE_ANALYTICS}`, () => {
const initialData = {
group: { fullPath: 'cool-group' },
......
......@@ -32,7 +32,7 @@ import {
endDate,
issueStage,
rawCustomStage,
transformedTasksByTypeData,
rawTasksByTypeData,
} from './mock_data';
const labelEventIds = labelEvents.map(ev => ev.identifier);
......@@ -239,9 +239,9 @@ describe('Cycle analytics utils', () => {
const groupBy = getDatesInRange(startDate, endDate, toYmd);
// only return the values, drop the date which is the first paramater
const extractSeriesValues = ({ series }) => series.map(kv => kv[1]);
const data = transformedTasksByTypeData.map(extractSeriesValues);
const data = rawTasksByTypeData.map(extractSeriesValues);
const labels = transformedTasksByTypeData.map(d => {
const labels = rawTasksByTypeData.map(d => {
const { label } = d;
return label.title;
});
......@@ -257,7 +257,7 @@ describe('Cycle analytics utils', () => {
describe('with data', () => {
beforeEach(() => {
transformed = getTasksByTypeData({ data: transformedTasksByTypeData, startDate, endDate });
transformed = getTasksByTypeData({ data: rawTasksByTypeData, startDate, endDate });
});
it('will return an object with the properties needed for the chart', () => {
......
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