Commit ee28174e authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Mike Greiling

Move vuex content to duration_chart module

Creates a new modules directory to
separate the cycle_analytics vuex store

Creates separate vuex files for the
duration chart component

Move vuex boilerplate into duration chart component
parent 0a0077c3
...@@ -53,7 +53,6 @@ export default { ...@@ -53,7 +53,6 @@ export default {
'isLoading', 'isLoading',
'isLoadingStage', 'isLoadingStage',
'isLoadingTasksByTypeChart', 'isLoadingTasksByTypeChart',
'isLoadingDurationChart',
'isEmptyStage', 'isEmptyStage',
'isSavingCustomStage', 'isSavingCustomStage',
'isCreatingCustomStage', 'isCreatingCustomStage',
...@@ -76,9 +75,7 @@ export default { ...@@ -76,9 +75,7 @@ export default {
...mapGetters([ ...mapGetters([
'hasNoAccessError', 'hasNoAccessError',
'currentGroupPath', 'currentGroupPath',
'durationChartPlottableData',
'tasksByTypeChartData', 'tasksByTypeChartData',
'durationChartMedianData',
'activeStages', 'activeStages',
'selectedProjectIds', 'selectedProjectIds',
'enableCustomOrdering', 'enableCustomOrdering',
...@@ -94,14 +91,11 @@ export default { ...@@ -94,14 +91,11 @@ export default {
return this.selectedGroup && !this.errorCode; return this.selectedGroup && !this.errorCode;
}, },
shouldDisplayDurationChart() { shouldDisplayDurationChart() {
return this.featureFlags.hasDurationChart && !this.hasNoAccessError; return this.featureFlags.hasDurationChart && !this.hasNoAccessError && !this.isLoading;
}, },
shouldDisplayTasksByTypeChart() { shouldDisplayTasksByTypeChart() {
return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError; return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError;
}, },
isDurationChartLoaded() {
return !this.isLoadingDurationChart && !this.isLoading;
},
isTasksByTypeChartLoaded() { isTasksByTypeChartLoaded() {
return !this.isLoading && !this.isLoadingTasksByTypeChart; return !this.isLoading && !this.isLoadingTasksByTypeChart;
}, },
...@@ -156,7 +150,6 @@ export default { ...@@ -156,7 +150,6 @@ export default {
'showEditCustomStageForm', 'showEditCustomStageForm',
'setDateRange', 'setDateRange',
'fetchTasksByTypeData', 'fetchTasksByTypeData',
'updateSelectedDurationChartStages',
'createCustomStage', 'createCustomStage',
'updateStage', 'updateStage',
'removeStage', 'removeStage',
...@@ -194,9 +187,6 @@ export default { ...@@ -194,9 +187,6 @@ export default {
onRemoveStage(id) { onRemoveStage(id) {
this.removeStage(id); this.removeStage(id);
}, },
onDurationStageSelect(stages) {
this.updateSelectedDurationChartStages(stages);
},
onStageReorder(data) { onStageReorder(data) {
this.reorderStage(data); this.reorderStage(data);
}, },
...@@ -321,13 +311,7 @@ export default { ...@@ -321,13 +311,7 @@ export default {
</div> </div>
</div> </div>
<div v-if="shouldDisplayDurationChart" class="mt-3"> <div v-if="shouldDisplayDurationChart" class="mt-3">
<duration-chart <duration-chart :stages="activeStages" />
:is-loading="isLoading"
:stages="activeStages"
:scatter-data="durationChartPlottableData"
:median-line-data="durationChartMedianData"
@stageSelected="onDurationStageSelect"
/>
</div> </div>
<template v-if="shouldDisplayTasksByTypeChart"> <template v-if="shouldDisplayTasksByTypeChart">
<div class="js-tasks-by-type-chart"> <div class="js-tasks-by-type-chart">
......
<script> <script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { dateFormats } from '../../shared/constants'; import { dateFormats } from '../../shared/constants';
import Scatterplot from '../../shared/components/scatterplot.vue'; import Scatterplot from '../../shared/components/scatterplot.vue';
...@@ -12,32 +13,25 @@ export default { ...@@ -12,32 +13,25 @@ export default {
StageDropdownFilter, StageDropdownFilter,
}, },
props: { props: {
isLoading: {
type: Boolean,
required: false,
default: false,
},
stages: { stages: {
type: Array, type: Array,
required: true, required: true,
}, },
scatterData: {
type: Array,
required: true,
},
medianLineData: {
type: Array,
required: true,
},
}, },
computed: { computed: {
...mapState('durationChart', ['isLoading']),
...mapGetters('durationChart', ['durationChartPlottableData', 'durationChartMedianData']),
hasData() { hasData() {
return Boolean(this.scatterData.length); return Boolean(this.durationChartPlottableData.length);
},
}, },
mounted() {
this.fetchDurationData();
}, },
methods: { methods: {
onSelectStage(selectedStages) { ...mapActions('durationChart', ['fetchDurationData', 'updateSelectedDurationChartStages']),
this.$emit('stageSelected', selectedStages); onDurationStageSelect(stages) {
this.updateSelectedDurationChartStages(stages);
}, },
}, },
durationChartTooltipDateFormat: dateFormats.defaultDate, durationChartTooltipDateFormat: dateFormats.defaultDate,
...@@ -53,7 +47,7 @@ export default { ...@@ -53,7 +47,7 @@ export default {
v-if="stages.length" v-if="stages.length"
class="ml-auto" class="ml-auto"
:stages="stages" :stages="stages"
@selected="onSelectStage" @selected="onDurationStageSelect"
/> />
</div> </div>
<scatterplot <scatterplot
...@@ -61,8 +55,8 @@ export default { ...@@ -61,8 +55,8 @@ export default {
:x-axis-title="s__('CycleAnalytics|Date')" :x-axis-title="s__('CycleAnalytics|Date')"
:y-axis-title="s__('CycleAnalytics|Total days to completion')" :y-axis-title="s__('CycleAnalytics|Total days to completion')"
:tooltip-date-format="$options.durationChartTooltipDateFormat" :tooltip-date-format="$options.durationChartTooltipDateFormat"
:scatter-data="scatterData" :scatter-data="durationChartPlottableData"
:median-line-data="medianLineData" :median-line-data="durationChartMedianData"
/> />
<div v-else ref="duration-chart-no-data" class="bs-callout bs-callout-info"> <div v-else ref="duration-chart-no-data" class="bs-callout bs-callout-info">
{{ __('There is no data available. Please change your selection.') }} {{ __('There is no data available. Please change your selection.') }}
......
import dateFormat from 'dateformat';
import Api from 'ee/api'; import Api from 'ee/api';
import { getDayDifference, getDateInPast } from '~/lib/utils/datetime_utility';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { dateFormats } from '../../shared/constants';
import { removeFlash } from '../utils'; import { removeFlash } from '../utils';
const handleErrorOrRethrow = ({ action, error }) => { const handleErrorOrRethrow = ({ action, error }) => {
...@@ -111,9 +108,8 @@ export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_CY ...@@ -111,9 +108,8 @@ export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_CY
export const receiveCycleAnalyticsDataSuccess = ({ state, commit, dispatch }) => { export const receiveCycleAnalyticsDataSuccess = ({ state, commit, dispatch }) => {
commit(types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS); commit(types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS);
const { featureFlags: { hasDurationChart = false, hasTasksByTypeChart = false } = {} } = state; const { featureFlags: { hasTasksByTypeChart = false } = {} } = state;
const promises = []; const promises = [];
if (hasDurationChart) promises.push('fetchDurationData');
if (hasTasksByTypeChart) promises.push('fetchTopRankedGroupLabels'); if (hasTasksByTypeChart) promises.push('fetchTopRankedGroupLabels');
return Promise.all(promises.map(func => dispatch(func))); return Promise.all(promises.map(func => dispatch(func)));
}; };
...@@ -405,125 +401,6 @@ export const removeStage = ({ dispatch, state }, stageId) => { ...@@ -405,125 +401,6 @@ export const removeStage = ({ dispatch, state }, stageId) => {
.catch(error => dispatch('receiveRemoveStageError', error)); .catch(error => dispatch('receiveRemoveStageError', error));
}; };
export const requestDurationData = ({ commit }) => commit(types.REQUEST_DURATION_DATA);
export const receiveDurationDataSuccess = ({ commit, state, dispatch }, data) => {
commit(types.RECEIVE_DURATION_DATA_SUCCESS, data);
const { featureFlags: { hasDurationChartMedian = false } = {} } = state;
if (hasDurationChartMedian) dispatch('fetchDurationMedianData');
};
export const receiveDurationDataError = ({ commit }) => {
commit(types.RECEIVE_DURATION_DATA_ERROR);
createFlash(__('There was an error while fetching value stream analytics duration data.'));
};
export const fetchDurationData = ({ state, dispatch, getters }) => {
dispatch('requestDurationData');
const {
stages,
selectedGroup: { fullPath },
} = state;
const {
cycleAnalyticsRequestParams: { created_after, created_before, project_ids },
} = getters;
return Promise.all(
stages.map(stage => {
const { slug } = stage;
return Api.cycleAnalyticsDurationChart(fullPath, slug, {
created_after,
created_before,
project_ids,
}).then(({ data }) => ({
slug,
selected: true,
data,
}));
}),
)
.then(data => {
dispatch('receiveDurationDataSuccess', data);
})
.catch(() => dispatch('receiveDurationDataError'));
};
export const requestDurationMedianData = ({ commit }) => commit(types.REQUEST_DURATION_MEDIAN_DATA);
export const receiveDurationMedianDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS, data);
export const receiveDurationMedianDataError = ({ commit }) => {
commit(types.RECEIVE_DURATION_MEDIAN_DATA_ERROR);
createFlash(__('There was an error while fetching value stream analytics duration median data.'));
};
export const fetchDurationMedianData = ({ state, dispatch, getters }) => {
dispatch('requestDurationMedianData');
const {
stages,
selectedGroup: { fullPath },
startDate,
endDate,
} = state;
const {
cycleAnalyticsRequestParams: { project_ids },
} = getters;
const offsetValue = getDayDifference(new Date(startDate), new Date(endDate));
const offsetCreatedAfter = getDateInPast(new Date(startDate), offsetValue);
const offsetCreatedBefore = getDateInPast(new Date(endDate), offsetValue);
return Promise.all(
stages.map(stage => {
const { slug } = stage;
return Api.cycleAnalyticsDurationChart(fullPath, slug, {
created_after: dateFormat(offsetCreatedAfter, dateFormats.isoDate),
created_before: dateFormat(offsetCreatedBefore, dateFormats.isoDate),
project_ids,
}).then(({ data }) => ({
slug,
selected: true,
data,
}));
}),
)
.then(data => {
dispatch('receiveDurationMedianDataSuccess', data);
})
.catch(() => dispatch('receiveDurationMedianDataError'));
};
export const updateSelectedDurationChartStages = ({ state, commit }, stages) => {
const setSelectedPropertyOnStages = data =>
data.map(stage => {
const selected = stages.reduce((result, object) => {
if (object.slug === stage.slug) return true;
return result;
}, false);
return {
...stage,
selected,
};
});
const { durationData, durationMedianData } = state;
const updatedDurationStageData = setSelectedPropertyOnStages(durationData);
const updatedDurationStageMedianData = setSelectedPropertyOnStages(durationMedianData);
commit(types.UPDATE_SELECTED_DURATION_CHART_STAGES, {
updatedDurationStageData,
updatedDurationStageMedianData,
});
};
export const setTasksByTypeFilters = ({ dispatch, commit }, data) => { export const setTasksByTypeFilters = ({ dispatch, commit }, data) => {
commit(types.SET_TASKS_BY_TYPE_FILTERS, data); commit(types.SET_TASKS_BY_TYPE_FILTERS, data);
dispatch('fetchTasksByTypeData'); dispatch('fetchTasksByTypeData');
......
...@@ -2,7 +2,7 @@ import dateFormat from 'dateformat'; ...@@ -2,7 +2,7 @@ import dateFormat from 'dateformat';
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import { dateFormats } from '../../shared/constants'; import { dateFormats } from '../../shared/constants';
import { getDurationChartData, getDurationChartMedianData, getTasksByTypeData } from '../utils'; import { getTasksByTypeData } from '../utils';
export const hasNoAccessError = state => state.errorCode === httpStatus.FORBIDDEN; export const hasNoAccessError = state => state.errorCode === httpStatus.FORBIDDEN;
...@@ -18,26 +18,6 @@ export const cycleAnalyticsRequestParams = ({ startDate = null, endDate = null } ...@@ -18,26 +18,6 @@ export const cycleAnalyticsRequestParams = ({ startDate = null, endDate = null }
created_before: endDate ? dateFormat(endDate, dateFormats.isoDate) : null, created_before: endDate ? dateFormat(endDate, dateFormats.isoDate) : null,
}); });
export const durationChartPlottableData = state => {
const { durationData, startDate, endDate } = state;
const selectedStagesDurationData = durationData.filter(stage => stage.selected);
const plottableData = getDurationChartData(selectedStagesDurationData, startDate, endDate);
return plottableData.length ? plottableData : [];
};
export const durationChartMedianData = state => {
const { durationMedianData, startDate, endDate } = state;
const selectedStagesDurationMedianData = durationMedianData.filter(stage => stage.selected);
const plottableData = getDurationChartMedianData(
selectedStagesDurationMedianData,
startDate,
endDate,
);
return plottableData.length ? plottableData : [];
};
export const tasksByTypeChartData = ({ tasksByType, startDate, endDate }) => { export const tasksByTypeChartData = ({ tasksByType, startDate, endDate }) => {
if (tasksByType && tasksByType.data.length) { if (tasksByType && tasksByType.data.length) {
return getTasksByTypeData({ return getTasksByTypeData({
......
...@@ -4,6 +4,7 @@ import * as actions from './actions'; ...@@ -4,6 +4,7 @@ import * as actions from './actions';
import * as getters from './getters'; import * as getters from './getters';
import mutations from './mutations'; import mutations from './mutations';
import state from './state'; import state from './state';
import durationChart from './modules/duration_chart/index';
Vue.use(Vuex); Vue.use(Vuex);
...@@ -13,4 +14,5 @@ export default () => ...@@ -13,4 +14,5 @@ export default () =>
getters, getters,
mutations, mutations,
state, state,
modules: { durationChart },
}); });
import dateFormat from 'dateformat';
import Api from 'ee/api';
import { getDayDifference, getDateInPast } from '~/lib/utils/datetime_utility';
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
import { dateFormats } from '../../../../shared/constants';
export const requestDurationData = ({ commit }) => commit(types.REQUEST_DURATION_DATA);
export const receiveDurationDataSuccess = ({ commit, rootState, dispatch }, data) => {
commit(types.RECEIVE_DURATION_DATA_SUCCESS, data);
const { featureFlags: { hasDurationChartMedian = false } = {} } = rootState;
if (hasDurationChartMedian) dispatch('fetchDurationMedianData');
};
export const receiveDurationDataError = ({ commit }) => {
commit(types.RECEIVE_DURATION_DATA_ERROR);
createFlash(__('There was an error while fetching value stream analytics duration data.'));
};
export const fetchDurationData = ({ dispatch, rootGetters, rootState }) => {
dispatch('requestDurationData');
const {
stages,
selectedGroup: { fullPath },
} = rootState;
const {
cycleAnalyticsRequestParams: { created_after, created_before, project_ids },
} = rootGetters;
return Promise.all(
stages.map(stage => {
const { slug } = stage;
return Api.cycleAnalyticsDurationChart(fullPath, slug, {
created_after,
created_before,
project_ids,
}).then(({ data }) => ({
slug,
selected: true,
data,
}));
}),
)
.then(data => dispatch('receiveDurationDataSuccess', data))
.catch(() => dispatch('receiveDurationDataError'));
};
export const receiveDurationMedianDataSuccess = ({ commit }, data) =>
commit(types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS, data);
export const receiveDurationMedianDataError = ({ commit }) => {
commit(types.RECEIVE_DURATION_MEDIAN_DATA_ERROR);
createFlash(__('There was an error while fetching value stream analytics duration median data.'));
};
export const fetchDurationMedianData = ({ dispatch, rootState, rootGetters }) => {
const {
stages,
selectedGroup: { fullPath },
startDate,
endDate,
} = rootState;
const {
cycleAnalyticsRequestParams: { project_ids },
} = rootGetters;
const offsetValue = getDayDifference(new Date(startDate), new Date(endDate));
const offsetCreatedAfter = getDateInPast(new Date(startDate), offsetValue);
const offsetCreatedBefore = getDateInPast(new Date(endDate), offsetValue);
return Promise.all(
stages.map(stage => {
const { slug } = stage;
return Api.cycleAnalyticsDurationChart(fullPath, slug, {
created_after: dateFormat(offsetCreatedAfter, dateFormats.isoDate),
created_before: dateFormat(offsetCreatedBefore, dateFormats.isoDate),
project_ids,
}).then(({ data }) => ({
slug,
selected: true,
data,
}));
}),
)
.then(data => dispatch('receiveDurationMedianDataSuccess', data))
.catch(() => dispatch('receiveDurationMedianDataError'));
};
export const updateSelectedDurationChartStages = ({ state, commit }, stages) => {
const setSelectedPropertyOnStages = data =>
data.map(stage => {
const selected = stages.reduce((result, object) => {
if (object.slug === stage.slug) return true;
return result;
}, false);
return {
...stage,
selected,
};
});
const { durationData, durationMedianData } = state;
const updatedDurationStageData = setSelectedPropertyOnStages(durationData);
const updatedDurationStageMedianData = setSelectedPropertyOnStages(durationMedianData);
commit(types.UPDATE_SELECTED_DURATION_CHART_STAGES, {
updatedDurationStageData,
updatedDurationStageMedianData,
});
};
import { getDurationChartData, getDurationChartMedianData } from '../../../utils';
export const durationChartPlottableData = (state, _, rootState) => {
const { startDate, endDate } = rootState;
const { durationData } = state;
const selectedStagesDurationData = durationData.filter(stage => stage.selected);
const plottableData = getDurationChartData(selectedStagesDurationData, startDate, endDate);
return plottableData.length ? plottableData : [];
};
export const durationChartMedianData = (state, _, rootState) => {
const { startDate, endDate } = rootState;
const { durationMedianData } = state;
const selectedStagesDurationMedianData = durationMedianData.filter(stage => stage.selected);
const plottableData = getDurationChartMedianData(
selectedStagesDurationMedianData,
startDate,
endDate,
);
return plottableData.length ? plottableData : [];
};
import state from './state';
import mutations from './mutations';
import * as getters from './getters';
import * as actions from './actions';
export default {
namespaced: true,
state,
mutations,
getters,
actions,
};
export const UPDATE_SELECTED_DURATION_CHART_STAGES = 'UPDATE_SELECTED_DURATION_CHART_STAGES';
export const REQUEST_DURATION_DATA = 'REQUEST_DURATION_DATA';
export const RECEIVE_DURATION_DATA_SUCCESS = 'RECEIVE_DURATION_DATA_SUCCESS';
export const RECEIVE_DURATION_DATA_ERROR = 'RECEIVE_DURATION_DATA_ERROR';
export const RECEIVE_DURATION_MEDIAN_DATA_SUCCESS = 'RECEIVE_DURATION_MEDIAN_DATA_SUCCESS';
export const RECEIVE_DURATION_MEDIAN_DATA_ERROR = 'RECEIVE_DURATION_MEDIAN_DATA_ERROR';
import * as types from './mutation_types';
export default {
[types.UPDATE_SELECTED_DURATION_CHART_STAGES](
state,
{ updatedDurationStageData, updatedDurationStageMedianData },
) {
state.durationData = updatedDurationStageData;
state.durationMedianData = updatedDurationStageMedianData;
},
[types.REQUEST_DURATION_DATA](state) {
state.isLoading = true;
},
[types.RECEIVE_DURATION_DATA_SUCCESS](state, data) {
state.durationData = data;
state.isLoading = false;
},
[types.RECEIVE_DURATION_DATA_ERROR](state) {
state.durationData = [];
state.isLoading = false;
},
[types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS](state, data) {
state.durationMedianData = data;
},
[types.RECEIVE_DURATION_MEDIAN_DATA_ERROR](state) {
state.durationMedianData = [];
},
};
export default () => ({
isLoading: false,
durationData: [],
durationMedianData: [],
});
...@@ -5,8 +5,6 @@ export const SET_SELECTED_PROJECTS = 'SET_SELECTED_PROJECTS'; ...@@ -5,8 +5,6 @@ export const SET_SELECTED_PROJECTS = 'SET_SELECTED_PROJECTS';
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE'; export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
export const SET_DATE_RANGE = 'SET_DATE_RANGE'; export const SET_DATE_RANGE = 'SET_DATE_RANGE';
export const UPDATE_SELECTED_DURATION_CHART_STAGES = 'UPDATE_SELECTED_DURATION_CHART_STAGES';
export const REQUEST_CYCLE_ANALYTICS_DATA = 'REQUEST_CYCLE_ANALYTICS_DATA'; export const REQUEST_CYCLE_ANALYTICS_DATA = 'REQUEST_CYCLE_ANALYTICS_DATA';
export const RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS = 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS'; export const RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS = 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS';
export const RECEIVE_CYCLE_ANALYTICS_DATA_ERROR = 'RECEIVE_CYCLE_ANALYTICS_DATA_ERROR'; export const RECEIVE_CYCLE_ANALYTICS_DATA_ERROR = 'RECEIVE_CYCLE_ANALYTICS_DATA_ERROR';
...@@ -50,14 +48,6 @@ export const RECEIVE_UPDATE_STAGE_ERROR = 'RECEIVE_UPDATE_STAGE_ERROR'; ...@@ -50,14 +48,6 @@ export const RECEIVE_UPDATE_STAGE_ERROR = 'RECEIVE_UPDATE_STAGE_ERROR';
export const REQUEST_REMOVE_STAGE = 'REQUEST_REMOVE_STAGE'; export const REQUEST_REMOVE_STAGE = 'REQUEST_REMOVE_STAGE';
export const RECEIVE_REMOVE_STAGE_RESPONSE = 'RECEIVE_REMOVE_STAGE_RESPONSE'; export const RECEIVE_REMOVE_STAGE_RESPONSE = 'RECEIVE_REMOVE_STAGE_RESPONSE';
export const REQUEST_DURATION_DATA = 'REQUEST_DURATION_DATA';
export const RECEIVE_DURATION_DATA_SUCCESS = 'RECEIVE_DURATION_DATA_SUCCESS';
export const RECEIVE_DURATION_DATA_ERROR = 'RECEIVE_DURATION_DATA_ERROR';
export const REQUEST_DURATION_MEDIAN_DATA = 'REQUEST_DURATION_MEDIAN_DATA';
export const RECEIVE_DURATION_MEDIAN_DATA_SUCCESS = 'RECEIVE_DURATION_MEDIAN_DATA_SUCCESS';
export const RECEIVE_DURATION_MEDIAN_DATA_ERROR = 'RECEIVE_DURATION_MEDIAN_DATA_ERROR';
export const SET_TASKS_BY_TYPE_FILTERS = 'SET_TASKS_BY_TYPE_FILTERS'; export const SET_TASKS_BY_TYPE_FILTERS = 'SET_TASKS_BY_TYPE_FILTERS';
export const INITIALIZE_CYCLE_ANALYTICS = 'INITIALIZE_CYCLE_ANALYTICS'; export const INITIALIZE_CYCLE_ANALYTICS = 'INITIALIZE_CYCLE_ANALYTICS';
......
...@@ -21,13 +21,6 @@ export default { ...@@ -21,13 +21,6 @@ export default {
state.startDate = startDate; state.startDate = startDate;
state.endDate = endDate; state.endDate = endDate;
}, },
[types.UPDATE_SELECTED_DURATION_CHART_STAGES](
state,
{ updatedDurationStageData, updatedDurationStageMedianData },
) {
state.durationData = updatedDurationStageData;
state.durationMedianData = updatedDurationStageMedianData;
},
[types.REQUEST_CYCLE_ANALYTICS_DATA](state) { [types.REQUEST_CYCLE_ANALYTICS_DATA](state) {
state.isLoading = true; state.isLoading = true;
state.isCreatingCustomStage = false; state.isCreatingCustomStage = false;
...@@ -181,28 +174,6 @@ export default { ...@@ -181,28 +174,6 @@ export default {
[types.RECEIVE_REMOVE_STAGE_RESPONSE](state) { [types.RECEIVE_REMOVE_STAGE_RESPONSE](state) {
state.isLoading = false; state.isLoading = false;
}, },
[types.REQUEST_DURATION_DATA](state) {
state.isLoadingDurationChart = true;
},
[types.RECEIVE_DURATION_DATA_SUCCESS](state, data) {
state.durationData = data;
state.isLoadingDurationChart = false;
},
[types.RECEIVE_DURATION_DATA_ERROR](state) {
state.durationData = [];
state.isLoadingDurationChart = false;
},
[types.REQUEST_DURATION_MEDIAN_DATA](state) {
state.isLoadingDurationChartMedianData = true;
},
[types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS](state, data) {
state.durationMedianData = data;
state.isLoadingDurationChartMedianData = false;
},
[types.RECEIVE_DURATION_MEDIAN_DATA_ERROR](state) {
state.durationMedianData = [];
state.isLoadingDurationChartMedianData = false;
},
[types.SET_TASKS_BY_TYPE_FILTERS](state, { filter, value }) { [types.SET_TASKS_BY_TYPE_FILTERS](state, { filter, value }) {
const { const {
tasksByType: { selectedLabelIds, ...tasksByTypeRest }, tasksByType: { selectedLabelIds, ...tasksByTypeRest },
......
...@@ -9,8 +9,6 @@ export default () => ({ ...@@ -9,8 +9,6 @@ export default () => ({
isLoading: false, isLoading: false,
isLoadingStage: false, isLoadingStage: false,
isLoadingTasksByTypeChart: false, isLoadingTasksByTypeChart: false,
isLoadingDurationChart: false,
isLoadingDurationChartMedianData: false,
isEmptyStage: false, isEmptyStage: false,
errorCode: null, errorCode: null,
...@@ -41,7 +39,4 @@ export default () => ({ ...@@ -41,7 +39,4 @@ export default () => ({
selectedLabelIds: [], selectedLabelIds: [],
data: [], data: [],
}, },
durationData: [],
durationMedianData: [],
}); });
...@@ -176,7 +176,7 @@ describe('Cycle Analytics component', () => { ...@@ -176,7 +176,7 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false); displaysDateRangePicker(false);
}); });
it('does not display the summary table', () => { it('does not display the recent activity table', () => {
displaysRecentActivityCard(false); displaysRecentActivityCard(false);
}); });
...@@ -233,7 +233,7 @@ describe('Cycle Analytics component', () => { ...@@ -233,7 +233,7 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(true); displaysDateRangePicker(true);
}); });
it('displays the summary table', () => { it('displays the recent activity table', () => {
displaysRecentActivityCard(true); displaysRecentActivityCard(true);
}); });
...@@ -259,10 +259,6 @@ describe('Cycle Analytics component', () => { ...@@ -259,10 +259,6 @@ describe('Cycle Analytics component', () => {
startDate: mockData.startDate, startDate: mockData.startDate,
endDate: mockData.endDate, endDate: mockData.endDate,
}); });
wrapper.vm.$store.dispatch(
'receiveDurationDataSuccess',
mockData.transformedDurationData,
);
}); });
it('displays the duration chart', () => { it('displays the duration chart', () => {
...@@ -277,7 +273,6 @@ describe('Cycle Analytics component', () => { ...@@ -277,7 +273,6 @@ describe('Cycle Analytics component', () => {
opts: { opts: {
stubs: { stubs: {
'stage-event-list': true, 'stage-event-list': true,
'summary-table': true,
'add-stage-button': true, 'add-stage-button': true,
'stage-table-header': true, 'stage-table-header': true,
}, },
...@@ -335,7 +330,7 @@ describe('Cycle Analytics component', () => { ...@@ -335,7 +330,7 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false); displaysDateRangePicker(false);
}); });
it('does not display the summary table', () => { it('does not display the recent activity table', () => {
displaysRecentActivityCard(false); displaysRecentActivityCard(false);
}); });
...@@ -424,7 +419,6 @@ describe('Cycle Analytics component', () => { ...@@ -424,7 +419,6 @@ describe('Cycle Analytics component', () => {
overrides = {}, overrides = {},
mockFetchStageData = true, mockFetchStageData = true,
mockFetchStageMedian = true, mockFetchStageMedian = true,
mockFetchDurationData = true,
mockFetchTasksByTypeData = true, mockFetchTasksByTypeData = true,
mockFetchTopRankedGroupLabels = true, mockFetchTopRankedGroupLabels = true,
}) => { }) => {
...@@ -450,12 +444,6 @@ describe('Cycle Analytics component', () => { ...@@ -450,12 +444,6 @@ describe('Cycle Analytics component', () => {
.reply(defaultStatus, { ...mockData.tasksByTypeData }); .reply(defaultStatus, { ...mockData.tasksByTypeData });
} }
if (mockFetchDurationData) {
mock
.onGet(mockData.endpoints.durationData)
.reply(defaultStatus, [...mockData.rawDurationData]);
}
if (mockFetchStageMedian) { if (mockFetchStageMedian) {
mock.onGet(mockData.endpoints.stageMedian).reply(defaultStatus, { value: null }); mock.onGet(mockData.endpoints.stageMedian).reply(defaultStatus, { value: null });
} }
...@@ -536,22 +524,6 @@ describe('Cycle Analytics component', () => { ...@@ -536,22 +524,6 @@ describe('Cycle Analytics component', () => {
); );
}); });
it('will display an error if the fetchDurationData request fails', () => {
expect(findFlashError()).toBeNull();
mockRequestCycleAnalyticsData({
mockFetchDurationData: false,
});
wrapper.vm.onGroupSelect(mockData.group);
return waitForPromises().catch(() => {
expect(findFlashError().innerText.trim()).toEqual(
'There was an error while fetching value stream analytics duration data.',
);
});
});
it('will display an error if the fetchStageMedian request fails', () => { it('will display an error if the fetchStageMedian request fails', () => {
expect(findFlashError()).toBeNull(); expect(findFlashError()).toBeNull();
...@@ -563,7 +535,7 @@ describe('Cycle Analytics component', () => { ...@@ -563,7 +535,7 @@ describe('Cycle Analytics component', () => {
return waitForPromises().catch(() => { return waitForPromises().catch(() => {
expect(findFlashError().innerText.trim()).toEqual( expect(findFlashError().innerText.trim()).toEqual(
'There was an error while fetching value stream analytics duration data.', 'There was an error while fetching value stream analytics data.',
); );
}); });
}); });
......
import { shallowMount, mount } from '@vue/test-utils'; import Vuex from 'vuex';
import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import $ from 'jquery'; import $ from 'jquery';
import 'bootstrap'; import 'bootstrap';
import '~/gl_dropdown'; import '~/gl_dropdown';
import durationChartStore from 'ee/analytics/cycle_analytics/store/modules/duration_chart';
import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue'; import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue'; import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import StageDropdownFilter from 'ee/analytics/cycle_analytics/components/stage_dropdown_filter.vue'; import StageDropdownFilter from 'ee/analytics/cycle_analytics/components/stage_dropdown_filter.vue';
import { import {
allowedStages as stages, allowedStages as stages,
durationChartPlottableData as scatterData, durationChartPlottableData as durationData,
durationChartPlottableMedianData as medianLineData, durationChartPlottableMedianData as durationMedianData,
} from '../mock_data'; } from '../mock_data';
function createComponent({ mountFn = shallowMount, props = {}, stubs = {} } = {}) { const localVue = createLocalVue();
localVue.use(Vuex);
const actionSpies = {
fetchDurationData: jest.fn(),
updateSelectedDurationChartStages: jest.fn(),
};
const fakeStore = ({ initialGetters, initialState }) =>
new Vuex.Store({
modules: {
durationChart: {
...durationChartStore,
getters: {
durationChartPlottableData: () => durationData,
durationChartMedianData: () => durationMedianData,
...initialGetters,
},
state: {
isLoading: false,
...initialState,
},
},
},
});
function createComponent({
mountFn = shallowMount,
stubs = {},
initialState = {},
initialGetters = {},
props = {},
} = {}) {
return mountFn(DurationChart, { return mountFn(DurationChart, {
localVue,
store: fakeStore({ initialState, initialGetters }),
propsData: { propsData: {
isLoading: false,
stages, stages,
scatterData,
medianLineData,
...props, ...props,
}, },
methods: actionSpies,
stubs: { stubs: {
GlLoadingIcon: true, GlLoadingIcon: true,
Scatterplot: true, Scatterplot: true,
...@@ -82,25 +115,33 @@ describe('DurationChart', () => { ...@@ -82,25 +115,33 @@ describe('DurationChart', () => {
return openStageDropdown(wrapper).then(() => selectStage(wrapper, selectedIndex)); return openStageDropdown(wrapper).then(() => selectStage(wrapper, selectedIndex));
}); });
it('emits the stageSelected event', () => { it('calls the `updateSelectedDurationChartStages` action', () => {
expect(wrapper.emitted().stageSelected).toBeTruthy(); expect(actionSpies.updateSelectedDurationChartStages).toHaveBeenCalledWith(selectedStages);
});
}); });
it('toggles the selected stage', () => { describe('with no stages', () => {
expect(wrapper.emitted('stageSelected')[0]).toEqual([selectedStages]); beforeEach(() => {
wrapper = createComponent({
return selectStage(wrapper, selectedIndex).then(() => { mountFn: mount,
const [updatedStages] = wrapper.emitted('stageSelected')[1]; props: { stages: [] },
stages.forEach(stage => { stubs: { StageDropdownFilter: false },
expect(updatedStages).toContain(stage);
}); });
}); });
it('does not render the stage dropdown', () => {
expect(findStageDropdown(wrapper).exists()).toBe(false);
}); });
}); });
describe('with no chart data', () => { describe('with no chart data', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ props: { scatterData: [], medianLineData: [] } }); wrapper = createComponent({
initialGetters: {
durationChartPlottableData: () => [],
durationChartMedianData: () => [],
},
});
}); });
it('renders the no data available message', () => { it('renders the no data available message', () => {
...@@ -110,11 +151,12 @@ describe('DurationChart', () => { ...@@ -110,11 +151,12 @@ describe('DurationChart', () => {
}); });
}); });
describe('while loading', () => { describe('when isLoading=true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ props: { isLoading: true } }); wrapper = createComponent({ initialState: { isLoading: true } });
}); });
it('renders loading icon', () => {
it('renders a loader', () => {
expect(findLoader(wrapper).exists()).toBe(true); expect(findLoader(wrapper).exists()).toBe(true);
}); });
}); });
......
...@@ -40,6 +40,8 @@ export const group = { ...@@ -40,6 +40,8 @@ export const group = {
avatar_url: `${TEST_HOST}/images/home/nasa.svg`, avatar_url: `${TEST_HOST}/images/home/nasa.svg`,
}; };
export const selectedGroup = convertObjectPropsToCamelCase(group, { deep: true });
const getStageByTitle = (stages, title) => const getStageByTitle = (stages, title) =>
stages.find(stage => stage.title && stage.title.toLowerCase().trim() === title) || {}; stages.find(stage => stage.title && stage.title.toLowerCase().trim() === title) || {};
......
import * as getters from 'ee/analytics/cycle_analytics/store/getters'; import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import { import { startDate, endDate, allowedStages, selectedProjects } from '../mock_data';
startDate,
endDate,
transformedDurationData,
transformedDurationMedianData,
durationChartPlottableData,
durationChartPlottableMedianData,
allowedStages,
selectedProjects,
} from '../mock_data';
let state = null; let state = null;
...@@ -98,54 +89,6 @@ describe('Cycle analytics getters', () => { ...@@ -98,54 +89,6 @@ describe('Cycle analytics getters', () => {
}); });
}); });
describe('durationChartPlottableData', () => {
it('returns plottable data for selected stages', () => {
const stateWithDurationData = {
startDate,
endDate,
durationData: transformedDurationData,
};
expect(getters.durationChartPlottableData(stateWithDurationData)).toEqual(
durationChartPlottableData,
);
});
it('returns an empty array if there is no plottable data for the selected stages', () => {
const stateWithDurationData = {
startDate,
endDate,
durationData: [],
};
expect(getters.durationChartPlottableData(stateWithDurationData)).toEqual([]);
});
});
describe('durationChartPlottableMedianData', () => {
it('returns plottable median data for selected stages', () => {
const stateWithDurationMedianData = {
startDate,
endDate,
durationMedianData: transformedDurationMedianData,
};
expect(getters.durationChartMedianData(stateWithDurationMedianData)).toEqual(
durationChartPlottableMedianData,
);
});
it('returns an empty array if there is no plottable median data for the selected stages', () => {
const stateWithDurationMedianData = {
startDate,
endDate,
durationMedianData: [],
};
expect(getters.durationChartMedianData(stateWithDurationMedianData)).toEqual([]);
});
});
const hiddenStage = { ...allowedStages[2], hidden: true }; const hiddenStage = { ...allowedStages[2], hidden: true };
const givenStages = [allowedStages[0], allowedStages[1], hiddenStage]; const givenStages = [allowedStages[0], allowedStages[1], hiddenStage];
describe.each` describe.each`
......
import * as getters from 'ee/analytics/cycle_analytics/store/modules/duration_chart/getters';
import {
startDate,
endDate,
transformedDurationData,
transformedDurationMedianData,
durationChartPlottableData,
durationChartPlottableMedianData,
} from '../../../mock_data';
const rootState = {
startDate,
endDate,
};
describe('DurationChart getters', () => {
describe('durationChartPlottableData', () => {
it('returns plottable data for selected stages', () => {
const stateWithDurationData = {
durationData: transformedDurationData,
};
expect(getters.durationChartPlottableData(stateWithDurationData, getters, rootState)).toEqual(
durationChartPlottableData,
);
});
it('returns an empty array if there is no plottable data for the selected stages', () => {
const stateWithDurationData = {
durationData: [],
};
expect(getters.durationChartPlottableData(stateWithDurationData, getters, rootState)).toEqual(
[],
);
});
});
describe('durationChartPlottableMedianData', () => {
it('returns plottable median data for selected stages', () => {
const stateWithDurationMedianData = {
durationMedianData: transformedDurationMedianData,
};
expect(
getters.durationChartMedianData(stateWithDurationMedianData, getters, rootState),
).toEqual(durationChartPlottableMedianData);
});
it('returns an empty array if there is no plottable median data for the selected stages', () => {
const stateWithDurationMedianData = {
startDate,
endDate,
durationMedianData: [],
};
expect(
getters.durationChartMedianData(stateWithDurationMedianData, getters, rootState),
).toEqual([]);
});
});
});
import mutations from 'ee/analytics/cycle_analytics/store/modules/duration_chart/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/modules/duration_chart/mutation_types';
import { transformedDurationData, transformedDurationMedianData } from '../../../mock_data';
let state = null;
describe('DurationChart mutations', () => {
beforeEach(() => {
state = {};
});
afterEach(() => {
state = null;
});
it.each`
mutation | stateKey | value
${types.REQUEST_DURATION_DATA} | ${'isLoading'} | ${true}
${types.RECEIVE_DURATION_DATA_ERROR} | ${'isLoading'} | ${false}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
mutations[mutation](state);
expect(state[stateKey]).toEqual(value);
});
it.each`
mutation | payload | expectedState
${types.UPDATE_SELECTED_DURATION_CHART_STAGES} | ${{ updatedDurationStageData: transformedDurationData, updatedDurationStageMedianData: transformedDurationMedianData }} | ${{ durationData: transformedDurationData, durationMedianData: transformedDurationMedianData }}
`(
'$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => {
state = {
selectedGroup: { fullPath: 'rad-stage' },
};
mutations[mutation](state, payload);
expect(state).toMatchObject(expectedState);
},
);
describe(`${types.RECEIVE_DURATION_DATA_SUCCESS}`, () => {
it('sets the data correctly and falsifies isLoading', () => {
const stateWithData = {
isLoading: true,
durationData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_DATA_SUCCESS](stateWithData, transformedDurationData);
expect(stateWithData.isLoading).toBe(false);
expect(stateWithData.durationData).toBe(transformedDurationData);
});
});
describe(`${types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS}`, () => {
it('sets the data correctly', () => {
const stateWithData = {
durationMedianData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS](
stateWithData,
transformedDurationMedianData,
);
expect(stateWithData.durationMedianData).toBe(transformedDurationMedianData);
});
});
describe(`${types.RECEIVE_DURATION_MEDIAN_DATA_ERROR}`, () => {
it('sets durationMedianData to an empty array', () => {
const stateWithData = {
durationMedianData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_MEDIAN_DATA_ERROR](stateWithData);
expect(stateWithData.durationMedianData).toStrictEqual([]);
});
});
});
...@@ -16,9 +16,7 @@ import { ...@@ -16,9 +16,7 @@ import {
endDate, endDate,
customizableStagesAndEvents, customizableStagesAndEvents,
tasksByTypeData, tasksByTypeData,
transformedDurationData,
transformedTasksByTypeData, transformedTasksByTypeData,
transformedDurationMedianData,
selectedProjects, selectedProjects,
} from '../mock_data'; } from '../mock_data';
...@@ -72,11 +70,8 @@ describe('Cycle analytics mutations', () => { ...@@ -72,11 +70,8 @@ describe('Cycle analytics mutations', () => {
${types.RECEIVE_UPDATE_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false} ${types.RECEIVE_UPDATE_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${types.REQUEST_REMOVE_STAGE} | ${'isLoading'} | ${true} ${types.REQUEST_REMOVE_STAGE} | ${'isLoading'} | ${true}
${types.RECEIVE_REMOVE_STAGE_RESPONSE} | ${'isLoading'} | ${false} ${types.RECEIVE_REMOVE_STAGE_RESPONSE} | ${'isLoading'} | ${false}
${types.REQUEST_DURATION_DATA} | ${'isLoadingDurationChart'} | ${true}
${types.RECEIVE_DURATION_DATA_ERROR} | ${'isLoadingDurationChart'} | ${false}
${types.REQUEST_STAGE_MEDIANS} | ${'medians'} | ${{}} ${types.REQUEST_STAGE_MEDIANS} | ${'medians'} | ${{}}
${types.RECEIVE_STAGE_MEDIANS_ERROR} | ${'medians'} | ${{}} ${types.RECEIVE_STAGE_MEDIANS_ERROR} | ${'medians'} | ${{}}
${types.REQUEST_DURATION_MEDIAN_DATA} | ${'isLoadingDurationChartMedianData'} | ${true}
${types.INITIALIZE_CYCLE_ANALYTICS_SUCCESS} | ${'isLoading'} | ${false} ${types.INITIALIZE_CYCLE_ANALYTICS_SUCCESS} | ${'isLoading'} | ${false}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => { `('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
mutations[mutation](state); mutations[mutation](state);
...@@ -91,7 +86,6 @@ describe('Cycle analytics mutations', () => { ...@@ -91,7 +86,6 @@ describe('Cycle analytics mutations', () => {
${types.SET_SELECTED_PROJECTS} | ${selectedProjects} | ${{ selectedProjects }} ${types.SET_SELECTED_PROJECTS} | ${selectedProjects} | ${{ selectedProjects }}
${types.SET_DATE_RANGE} | ${{ startDate, endDate }} | ${{ startDate, endDate }} ${types.SET_DATE_RANGE} | ${{ startDate, endDate }} | ${{ startDate, endDate }}
${types.SET_SELECTED_STAGE} | ${{ id: 'first-stage' }} | ${{ selectedStage: { id: 'first-stage' } }} ${types.SET_SELECTED_STAGE} | ${{ id: 'first-stage' }} | ${{ selectedStage: { id: 'first-stage' } }}
${types.UPDATE_SELECTED_DURATION_CHART_STAGES} | ${{ updatedDurationStageData: transformedDurationData, updatedDurationStageMedianData: transformedDurationMedianData }} | ${{ durationData: transformedDurationData, durationMedianData: transformedDurationMedianData }}
`( `(
'$mutation with payload $payload will update state with $expectedState', '$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => { ({ mutation, payload, expectedState }) => {
...@@ -197,51 +191,6 @@ describe('Cycle analytics mutations', () => { ...@@ -197,51 +191,6 @@ describe('Cycle analytics mutations', () => {
}); });
}); });
describe(`${types.RECEIVE_DURATION_DATA_SUCCESS}`, () => {
it('sets the data correctly and falsifies isLoadingDurationChart', () => {
const stateWithData = {
isLoadingDurationChart: true,
durationData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_DATA_SUCCESS](stateWithData, transformedDurationData);
expect(stateWithData.isLoadingDurationChart).toBe(false);
expect(stateWithData.durationData).toBe(transformedDurationData);
});
});
describe(`${types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS}`, () => {
it('sets the data correctly and falsifies isLoadingDurationChartMedianData', () => {
const stateWithData = {
isLoadingDurationChartMedianData: true,
durationMedianData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_MEDIAN_DATA_SUCCESS](
stateWithData,
transformedDurationMedianData,
);
expect(stateWithData.isLoadingDurationChartMedianData).toBe(false);
expect(stateWithData.durationMedianData).toBe(transformedDurationMedianData);
});
});
describe(`${types.RECEIVE_DURATION_MEDIAN_DATA_ERROR}`, () => {
it('falsifies isLoadingDurationChartMedianData and sets durationMedianData to an empty array', () => {
const stateWithData = {
isLoadingDurationChartMedianData: true,
durationMedianData: [['something', 'random']],
};
mutations[types.RECEIVE_DURATION_MEDIAN_DATA_ERROR](stateWithData);
expect(stateWithData.isLoadingDurationChartMedianData).toBe(false);
expect(stateWithData.durationMedianData).toStrictEqual([]);
});
});
describe(`${types.RECEIVE_STAGE_MEDIANS_SUCCESS}`, () => { describe(`${types.RECEIVE_STAGE_MEDIANS_SUCCESS}`, () => {
it('sets each id as a key in the median object with the corresponding value', () => { it('sets each id as a key in the median object with the corresponding value', () => {
const stateWithData = { const stateWithData = {
......
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