Commit 74824494 authored by Martin Wortschack's avatar Martin Wortschack Committed by Ezekiel Kigbo

[VSA] Show record count for the stage (FE)

parent 4c103b09
...@@ -77,6 +77,7 @@ export default { ...@@ -77,6 +77,7 @@ export default {
'cycleAnalyticsRequestParams', 'cycleAnalyticsRequestParams',
'pathNavigationData', 'pathNavigationData',
'isOverviewStageSelected', 'isOverviewStageSelected',
'selectedStageCount',
]), ]),
...mapGetters('customStages', ['customStageFormActive']), ...mapGetters('customStages', ['customStageFormActive']),
shouldRenderEmptyState() { shouldRenderEmptyState() {
...@@ -262,6 +263,7 @@ export default { ...@@ -262,6 +263,7 @@ export default {
:is-loading="isLoading || isLoadingStage" :is-loading="isLoading || isLoadingStage"
:stage-events="currentStageEvents" :stage-events="currentStageEvents"
:selected-stage="selectedStage" :selected-stage="selectedStage"
:stage-count="selectedStageCount"
:empty-state-message="selectedStageError" :empty-state-message="selectedStageError"
:no-data-svg-path="noDataSvgPath" :no-data-svg-path="noDataSvgPath"
:pagination="pagination" :pagination="pagination"
......
...@@ -37,6 +37,9 @@ export default { ...@@ -37,6 +37,9 @@ export default {
showPopover({ id }) { showPopover({ id }) {
return id && id !== OVERVIEW_STAGE_ID; return id && id !== OVERVIEW_STAGE_ID;
}, },
hasStageCount({ stageCount }) {
return stageCount !== null;
},
}, },
popoverOptions: { popoverOptions: {
triggers: 'hover', triggers: 'hover',
...@@ -64,6 +67,19 @@ export default { ...@@ -64,6 +67,19 @@ export default {
<div class="gl-pb-4 gl-font-weight-bold">{{ pathItem.metric }}</div> <div class="gl-pb-4 gl-font-weight-bold">{{ pathItem.metric }}</div>
</div> </div>
</div> </div>
<div class="gl-px-4">
<div class="gl-display-flex gl-justify-content-space-between">
<div class="gl-pr-4 gl-pb-4">
{{ s__('ValueStreamEvent|Items in stage') }}
</div>
<div class="gl-pb-4 gl-font-weight-bold">
<template v-if="hasStageCount(pathItem)">{{
n__('%d item', '%d items', pathItem.stageCount)
}}</template>
<template v-else>-</template>
</div>
</div>
</div>
<div class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"> <div class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50">
<div <div
v-if="pathItem.startEventHtmlDescription" v-if="pathItem.startEventHtmlDescription"
......
<script> <script>
import { GlEmptyState, GlIcon, GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui'; import {
GlEmptyState,
GlIcon,
GlLink,
GlLoadingIcon,
GlPagination,
GlTable,
GlBadge,
} from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { import {
NOT_ENOUGH_DATA_ERROR, NOT_ENOUGH_DATA_ERROR,
...@@ -31,6 +39,7 @@ export default { ...@@ -31,6 +39,7 @@ export default {
GlLoadingIcon, GlLoadingIcon,
GlPagination, GlPagination,
GlTable, GlTable,
GlBadge,
TotalTime, TotalTime,
}, },
props: { props: {
...@@ -46,6 +55,11 @@ export default { ...@@ -46,6 +55,11 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
stageCount: {
type: Number,
required: false,
default: null,
},
noDataSvgPath: { noDataSvgPath: {
type: String, type: String,
required: true, required: true,
...@@ -157,6 +171,10 @@ export default { ...@@ -157,6 +171,10 @@ export default {
:empty-text="emptyStateMessage" :empty-text="emptyStateMessage"
@sort-changed="onSort" @sort-changed="onSort"
> >
<template #head(end_event)="data">
<span>{{ data.label }}</span
><gl-badge v-if="stageCount" class="gl-ml-2" size="sm">{{ stageCount }}</gl-badge>
</template>
<template #cell(end_event)="{ item }"> <template #cell(end_event)="{ item }">
<div data-testid="vsa-stage-event"> <div data-testid="vsa-stage-event">
<div v-if="item.id" data-testid="vsa-stage-content"> <div v-if="item.id" data-testid="vsa-stage-content">
......
...@@ -129,6 +129,43 @@ export const fetchStageMedianValues = ({ dispatch, commit, getters }) => { ...@@ -129,6 +129,43 @@ export const fetchStageMedianValues = ({ dispatch, commit, getters }) => {
.catch((error) => dispatch('receiveStageMedianValuesError', error)); .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 }) => {
...@@ -430,11 +467,17 @@ export const receiveValueStreamsSuccess = ( ...@@ -430,11 +467,17 @@ export const receiveValueStreamsSuccess = (
data = [], data = [],
) => { ) => {
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data); commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
if (!selectedValueStream && data.length) { if (!selectedValueStream && data.length) {
const [firstStream] = data; const [firstStream] = data;
return dispatch('setSelectedValueStream', firstStream); return Promise.resolve()
.then(() => dispatch('setSelectedValueStream', firstStream))
.then(() => dispatch('fetchStageCountValues'));
} }
return dispatch(FETCH_VALUE_STREAM_DATA);
return Promise.resolve()
.then(() => dispatch(FETCH_VALUE_STREAM_DATA))
.then(() => dispatch('fetchStageCountValues'));
}; };
export const fetchValueStreams = ({ commit, dispatch, getters }) => { export const fetchValueStreams = ({ commit, dispatch, getters }) => {
......
...@@ -77,9 +77,13 @@ export const isOverviewStageSelected = ({ selectedStage }) => ...@@ -77,9 +77,13 @@ export const isOverviewStageSelected = ({ selectedStage }) =>
* *
* https://gitlab.com/gitlab-org/gitlab/-/issues/216227 * https://gitlab.com/gitlab-org/gitlab/-/issues/216227
*/ */
export const pathNavigationData = ({ stages, medians, selectedStage }) => export const pathNavigationData = ({ stages, medians, stageCounts, selectedStage }) =>
transformStagesForPathNavigation({ transformStagesForPathNavigation({
stages: [OVERVIEW_STAGE_CONFIG, ...filterStagesByHiddenStatus(stages, false)], stages: [OVERVIEW_STAGE_CONFIG, ...filterStagesByHiddenStatus(stages, false)],
medians, medians,
stageCounts,
selectedStage, selectedStage,
}); });
export const selectedStageCount = ({ selectedStage, stageCounts }) =>
stageCounts[selectedStage.id] || null;
...@@ -18,6 +18,10 @@ export const REQUEST_STAGE_MEDIANS = 'REQUEST_STAGE_MEDIANS'; ...@@ -18,6 +18,10 @@ export const REQUEST_STAGE_MEDIANS = 'REQUEST_STAGE_MEDIANS';
export const RECEIVE_STAGE_MEDIANS_SUCCESS = 'RECEIVE_STAGE_MEDIANS_SUCCESS'; export const RECEIVE_STAGE_MEDIANS_SUCCESS = 'RECEIVE_STAGE_MEDIANS_SUCCESS';
export const RECEIVE_STAGE_MEDIANS_ERROR = 'RECEIVE_STAGE_MEDIANS_ERROR'; export const RECEIVE_STAGE_MEDIANS_ERROR = 'RECEIVE_STAGE_MEDIANS_ERROR';
export const REQUEST_STAGE_COUNTS = 'REQUEST_STAGE_COUNTS';
export const RECEIVE_STAGE_COUNTS_SUCCESS = 'RECEIVE_STAGE_COUNTS_SUCCESS';
export const RECEIVE_STAGE_COUNTS_ERROR = 'RECEIVE_STAGE_COUNTS_ERROR';
export const REQUEST_GROUP_STAGES = 'REQUEST_GROUP_STAGES'; export const REQUEST_GROUP_STAGES = 'REQUEST_GROUP_STAGES';
export const RECEIVE_GROUP_STAGES_SUCCESS = 'RECEIVE_GROUP_STAGES_SUCCESS'; export const RECEIVE_GROUP_STAGES_SUCCESS = 'RECEIVE_GROUP_STAGES_SUCCESS';
export const RECEIVE_GROUP_STAGES_ERROR = 'RECEIVE_GROUP_STAGES_ERROR'; export const RECEIVE_GROUP_STAGES_ERROR = 'RECEIVE_GROUP_STAGES_ERROR';
......
...@@ -56,6 +56,21 @@ export default { ...@@ -56,6 +56,21 @@ export default {
[types.RECEIVE_STAGE_MEDIANS_ERROR](state) { [types.RECEIVE_STAGE_MEDIANS_ERROR](state) {
state.medians = {}; state.medians = {};
}, },
[types.REQUEST_STAGE_COUNTS](state) {
state.stageCounts = {};
},
[types.RECEIVE_STAGE_COUNTS_SUCCESS](state, stageCounts = []) {
state.stageCounts = stageCounts.reduce(
(acc, { id, count }) => ({
...acc,
[id]: count,
}),
{},
);
},
[types.RECEIVE_STAGE_COUNTS_ERROR](state) {
state.stageCounts = {};
},
[types.REQUEST_GROUP_STAGES](state) { [types.REQUEST_GROUP_STAGES](state) {
state.stages = []; state.stages = [];
}, },
......
...@@ -43,4 +43,5 @@ export default () => ({ ...@@ -43,4 +43,5 @@ export default () => ({
sort: PAGINATION_SORT_FIELD_END_EVENT, sort: PAGINATION_SORT_FIELD_END_EVENT,
direction: PAGINATION_SORT_DIRECTION_DESC, direction: PAGINATION_SORT_DIRECTION_DESC,
}, },
stageCounts: {},
}); });
...@@ -432,14 +432,21 @@ export const formatMedianValuesWithOverview = (medians = []) => { ...@@ -432,14 +432,21 @@ export const formatMedianValuesWithOverview = (medians = []) => {
* *
* @param {Array} stages - The stages available to the group / project * @param {Array} stages - The stages available to the group / project
* @param {Object} medians - The median values for the stages available to the group / project * @param {Object} medians - The median values for the stages available to the group / project
* @param {Object} stageCounts - The total item count for the stages available
* @param {Object} selectedStage - The currently selected stage * @param {Object} selectedStage - The currently selected stage
* @returns {Array} An array of stages formatted with data required for the path navigation * @returns {Array} An array of stages formatted with data required for the path navigation
*/ */
export const transformStagesForPathNavigation = ({ stages, medians, selectedStage }) => { export const transformStagesForPathNavigation = ({
stages,
medians,
stageCounts,
selectedStage,
}) => {
const formattedStages = stages.map((stage) => { const formattedStages = stages.map((stage) => {
return { return {
metric: medians[stage?.id], metric: medians[stage?.id],
selected: stage.id === selectedStage.id, selected: stage.id === selectedStage.id,
stageCount: stageCounts[stage?.id],
icon: null, icon: null,
...stage, ...stage,
}; };
......
...@@ -26,6 +26,8 @@ export default { ...@@ -26,6 +26,8 @@ export default {
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/records', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/records',
cycleAnalyticsStageMedianPath: cycleAnalyticsStageMedianPath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/median', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/median',
cycleAnalyticsStageCountPath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id/count',
cycleAnalyticsStagePath: cycleAnalyticsStagePath:
'/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id', '/groups/:id/-/analytics/value_stream_analytics/value_streams/:value_stream_id/stages/:stage_id',
cycleAnalyticsDurationChartPath: cycleAnalyticsDurationChartPath:
...@@ -187,6 +189,15 @@ export default { ...@@ -187,6 +189,15 @@ export default {
return axios.get(url, { params }); return axios.get(url, { params });
}, },
cycleAnalyticsStageCount({ groupId, valueStreamId, stageId, params = {} }) {
const url = Api.buildUrl(this.cycleAnalyticsStageCountPath)
.replace(':id', groupId)
.replace(':value_stream_id', valueStreamId)
.replace(':stage_id', stageId);
return axios.get(url, { params });
},
cycleAnalyticsCreateStage({ groupId, valueStreamId, data }) { cycleAnalyticsCreateStage({ groupId, valueStreamId, data }) {
const url = Api.buildUrl(this.cycleAnalyticsGroupStagesAndEventsPath) const url = Api.buildUrl(this.cycleAnalyticsGroupStagesAndEventsPath)
.replace(':id', groupId) .replace(':id', groupId)
......
---
title: Add stage count to group-level VSA
merge_request: 60598
author:
type: added
...@@ -91,6 +91,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot ...@@ -91,6 +91,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
</div> </div>
</div> </div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
172800 items
</div>
</div>
</div>
<div <div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50" class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
> >
...@@ -183,6 +205,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot ...@@ -183,6 +205,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
</div> </div>
</div> </div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
86400 items
</div>
</div>
</div>
<div <div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50" class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
> >
...@@ -275,6 +319,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot ...@@ -275,6 +319,28 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
</div> </div>
</div> </div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
129600 items
</div>
</div>
</div>
<div <div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50" class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
> >
......
...@@ -24,6 +24,7 @@ const fixtureEndpoints = { ...@@ -24,6 +24,7 @@ const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents: 'analytics/value_stream_analytics/stages.json', // customizable stages and events endpoint customizableCycleAnalyticsStagesAndEvents: 'analytics/value_stream_analytics/stages.json', // customizable stages and events endpoint
stageEvents: (stage) => `analytics/value_stream_analytics/stages/${stage}/records.json`, stageEvents: (stage) => `analytics/value_stream_analytics/stages/${stage}/records.json`,
stageMedian: (stage) => `analytics/value_stream_analytics/stages/${stage}/median.json`, stageMedian: (stage) => `analytics/value_stream_analytics/stages/${stage}/median.json`,
stageCount: (stage) => `analytics/value_stream_analytics/stages/${stage}/count.json`,
recentActivityData: 'analytics/metrics/value_stream_analytics/summary.json', recentActivityData: 'analytics/metrics/value_stream_analytics/summary.json',
timeMetricsData: 'analytics/metrics/value_stream_analytics/time_summary.json', timeMetricsData: 'analytics/metrics/value_stream_analytics/time_summary.json',
groupLabels: 'api/group_labels.json', groupLabels: 'api/group_labels.json',
...@@ -36,6 +37,7 @@ export const endpoints = { ...@@ -36,6 +37,7 @@ export const endpoints = {
durationData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/average_duration_chart/, durationData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/average_duration_chart/,
stageData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/records/, stageData: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/records/,
stageMedian: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/median/, stageMedian: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/median/,
stageCount: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages\/\w+\/count/,
baseStagesEndpoint: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages$/, baseStagesEndpoint: /analytics\/value_stream_analytics\/value_streams\/\w+\/stages$/,
tasksByTypeData: /analytics\/type_of_work\/tasks_by_type/, tasksByTypeData: /analytics\/type_of_work\/tasks_by_type/,
tasksByTypeTopLabelsData: /analytics\/type_of_work\/tasks_by_type\/top_labels/, tasksByTypeTopLabelsData: /analytics\/type_of_work\/tasks_by_type\/top_labels/,
...@@ -142,6 +144,29 @@ export const stageMediansWithNumericIds = rawStageMedians.reduce((acc, { id, val ...@@ -142,6 +144,29 @@ export const stageMediansWithNumericIds = rawStageMedians.reduce((acc, { id, val
}; };
}, {}); }, {});
export const rawStageCounts = defaultStages.map((id) => ({
id,
...getJSONFixture(fixtureEndpoints.stageCount(id)),
}));
// Once https://gitlab.com/gitlab-org/gitlab/-/issues/328422 is fixed
// we should be able to use the rawStageCounts for building
// the stage counts mock data
/*
export const stageCounts = rawStageCounts.reduce(
(acc, { id, value }) => ({
...acc,
[id]: value,
}),
{},
);
*/
export const stageCounts = rawStageMedians.reduce((acc, { id, value }) => {
const { id: stageId } = getStageByTitle(dummyState.stages, id);
return { ...acc, [stageId]: value };
}, {});
export const endDate = new Date(2019, 0, 14); export const endDate = new Date(2019, 0, 14);
export const startDate = getDateInPast(endDate, DEFAULT_DAYS_IN_PAST); export const startDate = getDateInPast(endDate, DEFAULT_DAYS_IN_PAST);
...@@ -215,6 +240,7 @@ export const transformedTasksByTypeData = getTasksByTypeData(apiTasksByTypeData) ...@@ -215,6 +240,7 @@ export const transformedTasksByTypeData = getTasksByTypeData(apiTasksByTypeData)
export const transformedStagePathData = transformStagesForPathNavigation({ export const transformedStagePathData = transformStagesForPathNavigation({
stages: [{ ...OVERVIEW_STAGE_CONFIG }, ...allowedStages], stages: [{ ...OVERVIEW_STAGE_CONFIG }, ...allowedStages],
medians, medians,
stageCounts,
selectedStage: issueStage, selectedStage: issueStage,
}); });
......
...@@ -766,6 +766,37 @@ describe('Value Stream Analytics actions', () => { ...@@ -766,6 +766,37 @@ describe('Value Stream Analytics actions', () => {
}); });
}); });
describe('fetchStageCountValues', () => {
const fetchCountResponse = activeStages.map(({ slug: id }) => ({ events: [], id }));
beforeEach(() => {
state = {
...state,
stages,
currentGroup,
featureFlags: {
...state.featureFlags,
hasPathNavigation: true,
},
};
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;
...@@ -1176,7 +1207,7 @@ describe('Value Stream Analytics actions', () => { ...@@ -1176,7 +1207,7 @@ describe('Value Stream Analytics actions', () => {
}); });
describe('receiveValueStreamsSuccess', () => { describe('receiveValueStreamsSuccess', () => {
it(`with a selectedValueStream in state commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'fetchValueStreamData'`, () => { it(`with a selectedValueStream in state commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'fetchValueStreamData' and 'fetchStageCountValues'`, () => {
return testAction( return testAction(
actions.receiveValueStreamsSuccess, actions.receiveValueStreamsSuccess,
valueStreams, valueStreams,
...@@ -1187,11 +1218,11 @@ describe('Value Stream Analytics actions', () => { ...@@ -1187,11 +1218,11 @@ describe('Value Stream Analytics actions', () => {
payload: valueStreams, payload: valueStreams,
}, },
], ],
[{ type: 'fetchValueStreamData' }], [{ type: 'fetchValueStreamData' }, { type: 'fetchStageCountValues' }],
); );
}); });
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'setSelectedValueStream'`, () => { it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation and dispatches 'setSelectedValueStream' and 'fetchStageCountValues'`, () => {
return testAction( return testAction(
actions.receiveValueStreamsSuccess, actions.receiveValueStreamsSuccess,
valueStreams, valueStreams,
...@@ -1205,7 +1236,10 @@ describe('Value Stream Analytics actions', () => { ...@@ -1205,7 +1236,10 @@ describe('Value Stream Analytics actions', () => {
payload: valueStreams, payload: valueStreams,
}, },
], ],
[{ type: 'setSelectedValueStream', payload: selectedValueStream }], [
{ type: 'setSelectedValueStream', payload: selectedValueStream },
{ type: 'fetchStageCountValues' },
],
); );
}); });
}); });
......
...@@ -16,6 +16,7 @@ import { ...@@ -16,6 +16,7 @@ import {
transformedStagePathData, transformedStagePathData,
issueStage, issueStage,
stageMedians, stageMedians,
stageCounts,
basePaginationResult, basePaginationResult,
initialPaginationState, initialPaginationState,
} from '../mock_data'; } from '../mock_data';
...@@ -215,6 +216,7 @@ describe('Value Stream Analytics getters', () => { ...@@ -215,6 +216,7 @@ describe('Value Stream Analytics getters', () => {
stages: allowedStages, stages: allowedStages,
medians: stageMedians, medians: stageMedians,
selectedStage: issueStage, selectedStage: issueStage,
stageCounts,
}; };
expect(getters.pathNavigationData(state)).toEqual(transformedStagePathData); expect(getters.pathNavigationData(state)).toEqual(transformedStagePathData);
...@@ -240,4 +242,16 @@ describe('Value Stream Analytics getters', () => { ...@@ -240,4 +242,16 @@ describe('Value Stream Analytics getters', () => {
expect(getters.paginationParams(state)).toEqual({ ...basePaginationResult, page }); expect(getters.paginationParams(state)).toEqual({ ...basePaginationResult, page });
}); });
}); });
describe('selectedStageCount', () => {
it('returns the count when a value exist for the given stage', () => {
state = { selectedStage: { id: 1 }, stageCounts: { 1: 10, 2: 20 } };
expect(getters.selectedStageCount(state)).toEqual(10);
});
it('returns null if there is no value for the given stage', () => {
state = { selectedStage: { id: 3 }, stageCounts: { 1: 10, 2: 20 } };
expect(getters.selectedStageCount(state)).toEqual(null);
});
});
}); });
...@@ -63,6 +63,8 @@ describe('Value Stream Analytics mutations', () => { ...@@ -63,6 +63,8 @@ describe('Value Stream Analytics mutations', () => {
${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} | ${'deleteValueStreamError'} | ${null} ${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} | ${'deleteValueStreamError'} | ${null}
${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} | ${'selectedValueStream'} | ${null} ${types.RECEIVE_DELETE_VALUE_STREAM_SUCCESS} | ${'selectedValueStream'} | ${null}
${types.INITIALIZE_VALUE_STREAM_SUCCESS} | ${'isLoading'} | ${false} ${types.INITIALIZE_VALUE_STREAM_SUCCESS} | ${'isLoading'} | ${false}
${types.REQUEST_STAGE_COUNTS} | ${'stageCounts'} | ${{}}
${types.RECEIVE_STAGE_COUNTS_ERROR} | ${'stageCounts'} | ${{}}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => { `('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
mutations[mutation](state); mutations[mutation](state);
...@@ -197,6 +199,23 @@ describe('Value Stream Analytics mutations', () => { ...@@ -197,6 +199,23 @@ describe('Value Stream Analytics mutations', () => {
}); });
}); });
describe(`${types.RECEIVE_STAGE_COUNTS_SUCCESS}`, () => {
beforeEach(() => {
state = {
stageCounts: {},
};
mutations[types.RECEIVE_STAGE_COUNTS_SUCCESS](state, [
{ id: 1, count: 10 },
{ id: 2, count: 20 },
]);
});
it('sets each id as a key in the stageCounts object with the corresponding count', () => {
expect(state.stageCounts).toMatchObject({ 1: 10, 2: 20 });
});
});
describe(`${types.INITIALIZE_VSA}`, () => { describe(`${types.INITIALIZE_VSA}`, () => {
const initialData = { const initialData = {
group: { fullPath: 'cool-group' }, group: { fullPath: 'cool-group' },
......
...@@ -43,6 +43,7 @@ import { ...@@ -43,6 +43,7 @@ import {
pathNavIssueMetric, pathNavIssueMetric,
timeMetricsData, timeMetricsData,
rawStageMedians, rawStageMedians,
stageCounts,
} from './mock_data'; } from './mock_data';
const labelEventIds = labelEvents.map((ev) => ev.identifier); const labelEventIds = labelEvents.map((ev) => ev.identifier);
...@@ -346,6 +347,7 @@ describe('Value Stream Analytics utils', () => { ...@@ -346,6 +347,7 @@ describe('Value Stream Analytics utils', () => {
stages, stages,
medians: stageMediansWithNumericIds, medians: stageMediansWithNumericIds,
selectedStage: issueStage, selectedStage: issueStage,
stageCounts,
}); });
describe('transforms the data as expected', () => { describe('transforms the data as expected', () => {
......
...@@ -152,6 +152,13 @@ RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do ...@@ -152,6 +152,13 @@ RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
expect(response).to be_successful expect(response).to be_successful
end end
it "analytics/value_stream_analytics/stages/#{stage[:name]}/count.json" do
stage_id = group.cycle_analytics_stages.find_by(name: stage[:name]).id
get(:count, params: params.merge({ id: stage_id }), format: :json)
expect(response).to be_successful
end
end end
it "analytics/value_stream_analytics/stages/label-based-stage/records.json" do it "analytics/value_stream_analytics/stages/label-based-stage/records.json" do
...@@ -165,5 +172,11 @@ RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do ...@@ -165,5 +172,11 @@ RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
expect(response).to be_successful expect(response).to be_successful
end end
it "analytics/value_stream_analytics/stages/label-based-stage/count.json" do
get(:count, params: params.merge({ id: label_based_stage.id }), format: :json)
expect(response).to be_successful
end
end end
end end
...@@ -267,6 +267,11 @@ msgid_plural "%d issues successfully imported with the label" ...@@ -267,6 +267,11 @@ msgid_plural "%d issues successfully imported with the label"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "%d item"
msgid_plural "%d items"
msgstr[0] ""
msgstr[1] ""
msgid "%d layer" msgid "%d layer"
msgid_plural "%d layers" msgid_plural "%d layers"
msgstr[0] "" msgstr[0] ""
...@@ -35555,6 +35560,9 @@ msgstr "" ...@@ -35555,6 +35560,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Total number of deploys to production." msgid "ValueStreamAnalytics|Total number of deploys to production."
msgstr "" msgstr ""
msgid "ValueStreamEvent|Items in stage"
msgstr ""
msgid "ValueStreamEvent|Stage time (median)" msgid "ValueStreamEvent|Stage time (median)"
msgstr "" msgstr ""
......
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