Commit 73516183 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'ek-minor-vsa-refactor-old-code' into 'master'

Refactor VSA metrics components

See merge request gitlab-org/gitlab!39625
parents c3091d4b 548da9fb
...@@ -2,7 +2,6 @@ import $ from 'jquery'; ...@@ -2,7 +2,6 @@ import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import filterMixins from 'ee_else_ce/analytics/cycle_analytics/mixins/filter_mixins';
import Flash from '../flash'; import Flash from '../flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
...@@ -45,7 +44,6 @@ export default () => { ...@@ -45,7 +44,6 @@ export default () => {
import('ee_component/analytics/shared/components/date_range_dropdown.vue'), import('ee_component/analytics/shared/components/date_range_dropdown.vue'),
'stage-nav-item': stageNavItem, 'stage-nav-item': stageNavItem,
}, },
mixins: [filterMixins],
data() { data() {
return { return {
store: CycleAnalyticsStore, store: CycleAnalyticsStore,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants'; import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import { PROJECTS_PER_PAGE, STAGE_ACTIONS } from '../constants'; import { PROJECTS_PER_PAGE } from '../constants';
import GroupsDropdownFilter from '../../shared/components/groups_dropdown_filter.vue'; import GroupsDropdownFilter from '../../shared/components/groups_dropdown_filter.vue';
import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue'; import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue';
import { SIMILARITY_ORDER, LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants'; import { SIMILARITY_ORDER, LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants';
...@@ -12,14 +12,12 @@ import DurationChart from './duration_chart.vue'; ...@@ -12,14 +12,12 @@ import DurationChart from './duration_chart.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue'; import TypeOfWorkCharts from './type_of_work_charts.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue';
import { toYmd } from '../../shared/utils'; import { toYmd } from '../../shared/utils';
import RecentActivityCard from './recent_activity_card.vue';
import TimeMetricsCard from './time_metrics_card.vue';
import StageTableNav from './stage_table_nav.vue'; import StageTableNav from './stage_table_nav.vue';
import CustomStageForm from './custom_stage_form.vue'; import CustomStageForm from './custom_stage_form.vue';
import PathNavigation from './path_navigation.vue'; import PathNavigation from './path_navigation.vue';
import MetricCard from '../../shared/components/metric_card.vue';
import FilterBar from './filter_bar.vue'; import FilterBar from './filter_bar.vue';
import ValueStreamSelect from './value_stream_select.vue'; import ValueStreamSelect from './value_stream_select.vue';
import Metrics from './metrics.vue';
export default { export default {
name: 'CycleAnalytics', name: 'CycleAnalytics',
...@@ -32,15 +30,13 @@ export default { ...@@ -32,15 +30,13 @@ export default {
ProjectsDropdownFilter, ProjectsDropdownFilter,
StageTable, StageTable,
TypeOfWorkCharts, TypeOfWorkCharts,
RecentActivityCard,
TimeMetricsCard,
CustomStageForm, CustomStageForm,
StageTableNav, StageTableNav,
PathNavigation, PathNavigation,
MetricCard,
FilterBar, FilterBar,
ValueStreamSelect, ValueStreamSelect,
UrlSync, UrlSync,
Metrics,
}, },
props: { props: {
emptyStateSvgPath: { emptyStateSvgPath: {
...@@ -215,7 +211,6 @@ export default { ...@@ -215,7 +211,6 @@ export default {
min_access_level: featureAccessLevel.EVERYONE, min_access_level: featureAccessLevel.EVERYONE,
}, },
maxDateRange: DATE_RANGE_LIMIT, maxDateRange: DATE_RANGE_LIMIT,
STAGE_ACTIONS,
}; };
</script> </script>
<template> <template>
...@@ -304,23 +299,7 @@ export default { ...@@ -304,23 +299,7 @@ export default {
" "
/> />
<div v-else-if="!errorCode"> <div v-else-if="!errorCode">
<div class="js-recent-activity gl-mt-3 gl-display-flex"> <metrics :group-path="currentGroupPath" :request-params="cycleAnalyticsRequestParams" />
<div class="gl-flex-fill-1 gl-pr-2">
<time-metrics-card
#default="{ metrics, loading }"
:group-path="currentGroupPath"
:additional-params="cycleAnalyticsRequestParams"
>
<metric-card :title="__('Time')" :metrics="metrics" :is-loading="loading" />
</time-metrics-card>
</div>
<div class="gl-flex-fill-1 gl-pl-2">
<recent-activity-card
:group-path="currentGroupPath"
:additional-params="cycleAnalyticsRequestParams"
/>
</div>
</div>
<div v-if="isLoading"> <div v-if="isLoading">
<gl-loading-icon class="mt-4" size="md" /> <gl-loading-icon class="mt-4" size="md" />
</div> </div>
...@@ -356,7 +335,7 @@ export default { ...@@ -356,7 +335,7 @@ export default {
:events="formEvents" :events="formEvents"
@createStage="onCreateCustomStage" @createStage="onCreateCustomStage"
@updateStage="onUpdateCustomStage" @updateStage="onUpdateCustomStage"
@clearErrors="$emit('clearFormErrors')" @clearErrors="$emit('clear-form-errors')"
/> />
</template> </template>
</stage-table> </stage-table>
......
<script>
import { OVERVIEW_METRICS } from '../constants';
import TimeMetricsCard from './time_metrics_card.vue';
import MetricCard from '../../shared/components/metric_card.vue';
export default {
name: 'OverviewActivity',
components: {
TimeMetricsCard,
MetricCard,
},
props: {
groupPath: {
type: String,
required: true,
},
requestParams: {
type: Object,
required: true,
},
},
overviewMetrics: OVERVIEW_METRICS,
};
</script>
<template>
<div class="js-recent-activity gl-mt-3 gl-display-flex">
<div class="gl-flex-fill-1 gl-pr-2">
<time-metrics-card
#default="{ metrics, loading }"
:group-path="groupPath"
:additional-params="requestParams"
:request-type="$options.overviewMetrics.TIME_SUMMARY"
>
<metric-card :title="__('Time')" :metrics="metrics" :is-loading="loading" />
</time-metrics-card>
</div>
<div class="gl-flex-fill-1 gl-pl-2">
<time-metrics-card
#default="{ metrics, loading }"
:group-path="groupPath"
:additional-params="requestParams"
:request-type="$options.overviewMetrics.RECENT_ACTIVITY"
>
<metric-card :title="__('Recent Activity')" :metrics="metrics" :is-loading="loading" />
</time-metrics-card>
</div>
</div>
</template>
<script>
import Api from 'ee/api';
import { __ } from '~/locale';
import createFlash from '~/flash';
import MetricCard from '../../shared/components/metric_card.vue';
import { removeFlash, prepareTimeMetricsData } from '../utils';
export default {
name: 'RecentActivityCard',
components: {
MetricCard,
},
props: {
groupPath: {
type: String,
required: true,
},
additionalParams: {
type: Object,
required: false,
default: null,
},
},
data() {
return {
data: [],
loading: false,
};
},
watch: {
additionalParams() {
this.fetchData();
},
},
mounted() {
this.fetchData();
},
methods: {
fetchData() {
removeFlash();
this.loading = true;
return Api.cycleAnalyticsSummaryData(
this.groupPath,
this.additionalParams ? this.additionalParams : {},
)
.then(({ data }) => {
this.data = prepareTimeMetricsData(data);
})
.catch(() => {
createFlash(
__('There was an error while fetching value stream analytics recent activity data.'),
);
})
.finally(() => {
this.loading = false;
});
},
},
};
</script>
<template>
<metric-card :title="__('Recent Activity')" :metrics="data" :is-loading="loading" />
</template>
<script> <script>
import Api from 'ee/api'; import Api from 'ee/api';
import { __, s__ } from '~/locale'; import { sprintf, __, s__ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import MetricCard from '../../shared/components/metric_card.vue'; import MetricCard from '../../shared/components/metric_card.vue';
import { removeFlash, prepareTimeMetricsData } from '../utils'; import { removeFlash, prepareTimeMetricsData } from '../utils';
import { OVERVIEW_METRICS } from '../constants';
const I18N_TEXT = { const I18N_TEXT = {
'lead-time': s__('ValueStreamAnalytics|Median time from issue created to issue closed.'), 'lead-time': s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
'cycle-time': s__('ValueStreamAnalytics|Median time from first commit to issue closed.'), 'cycle-time': s__('ValueStreamAnalytics|Median time from first commit to issue closed.'),
}; };
const requestData = ({ requestType, groupPath, additionalParams }) => {
return requestType === OVERVIEW_METRICS.TIME_SUMMARY
? Api.cycleAnalyticsTimeSummaryData(groupPath, additionalParams)
: Api.cycleAnalyticsSummaryData(groupPath, additionalParams);
};
export default { export default {
name: 'TimeMetricsCard', name: 'TimeMetricsCard',
components: { components: {
...@@ -25,6 +32,11 @@ export default { ...@@ -25,6 +32,11 @@ export default {
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
requestType: {
type: String,
required: true,
validator: t => OVERVIEW_METRICS[t],
},
}, },
data() { data() {
return { return {
...@@ -44,13 +56,22 @@ export default { ...@@ -44,13 +56,22 @@ export default {
fetchData() { fetchData() {
removeFlash(); removeFlash();
this.loading = true; this.loading = true;
return Api.cycleAnalyticsTimeSummaryData(this.groupPath, this.additionalParams) return requestData(this)
.then(({ data }) => { .then(({ data }) => {
this.data = prepareTimeMetricsData(data, I18N_TEXT); this.data = prepareTimeMetricsData(data, I18N_TEXT);
}) })
.catch(() => { .catch(() => {
const requestTypeName =
this.requestType === OVERVIEW_METRICS.TIME_SUMMARY
? __('time summary')
: __('recent activity');
createFlash( createFlash(
__('There was an error while fetching value stream analytics time summary data.'), sprintf(
s__(
'There was an error while fetching value stream analytics %{requestTypeName} data.',
),
{ requestTypeName },
),
); );
}) })
.finally(() => { .finally(() => {
......
...@@ -78,3 +78,8 @@ export const CAPITALIZED_STAGE_NAME = Object.keys(STAGE_NAME).reduce((acc, stage ...@@ -78,3 +78,8 @@ export const CAPITALIZED_STAGE_NAME = Object.keys(STAGE_NAME).reduce((acc, stage
export const PATH_HOME_ICON = 'home'; export const PATH_HOME_ICON = 'home';
export const DEFAULT_VALUE_STREAM_ID = 'default'; export const DEFAULT_VALUE_STREAM_ID = 'default';
export const OVERVIEW_METRICS = {
TIME_SUMMARY: 'TIME_SUMMARY',
RECENT_ACTIVITY: 'RECENT_ACTIVITY',
};
export default {
data() {
return {
dateOptions: [7, 30, 90],
selectedGroup: null,
selectedProjectIds: [],
multiProjectSelect: true,
};
},
methods: {
renderSelectedGroup(selectedItemURL) {
this.service = this.createCycleAnalyticsService(selectedItemURL);
this.loadAnalyticsData();
},
setSelectedGroup(selectedGroup) {
this.selectedGroup = selectedGroup;
this.renderSelectedGroup(`/groups/${selectedGroup.path}/-/value_stream_analytics`);
},
setSelectedProjects(selectedProjects) {
this.selectedProjectIds = selectedProjects.map(value => value.id);
this.loadAnalyticsData();
},
setSelectedDate(days) {
if (this.startDate !== days) {
this.startDate = days;
this.loadAnalyticsData();
}
},
loadAnalyticsData() {
this.fetchCycleAnalyticsData({
startDate: this.startDate,
projectIds: this.selectedProjectIds,
});
},
},
};
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Metrics renders the recent activity 1`] = `"<time-metrics-card-stub grouppath=\\"foo\\" additionalparams=\\"[object Object]\\" requesttype=\\"RECENT_ACTIVITY\\"></time-metrics-card-stub>"`;
exports[`Metrics renders the time summary 1`] = `"<time-metrics-card-stub grouppath=\\"foo\\" additionalparams=\\"[object Object]\\" requesttype=\\"TIME_SUMMARY\\"></time-metrics-card-stub>"`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RecentActivityCard matches the snapshot 1`] = `
<div
class="card"
>
<!---->
<div
class="card-header"
>
<strong>
Recent Activity
</strong>
</div>
<div
class="card-body"
>
<!---->
<!---->
<div
class="gl-display-flex"
>
<div
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
>
4
</h3>
<p
class="text-secondary gl-font-sm gl-mb-2"
>
New Issues
<!---->
</p>
</div>
<div
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
>
-
</h3>
<p
class="text-secondary gl-font-sm gl-mb-2"
>
Deploys
<!---->
</p>
</div>
<div
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
>
-
</h3>
<p
class="text-secondary gl-font-sm gl-mb-2"
>
Deployment Frequency
<!---->
</p>
</div>
</div>
</div>
<!---->
<!---->
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TimeMetricsCard Recent activity renders the Recent activity metric 1`] = `"<div><span>4 </span><span>- </span><span>- per day</span></div>"`;
exports[`TimeMetricsCard Time summary renders the Time summary metric 1`] = `"<div><span>4.5 days</span><span>3.0 days</span></div>"`;
...@@ -7,8 +7,7 @@ import axios from 'axios'; ...@@ -7,8 +7,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import GroupsDropdownFilter from 'ee/analytics/shared/components/groups_dropdown_filter.vue'; import GroupsDropdownFilter from 'ee/analytics/shared/components/groups_dropdown_filter.vue';
import ProjectsDropdownFilter from 'ee/analytics/shared/components/projects_dropdown_filter.vue'; import ProjectsDropdownFilter from 'ee/analytics/shared/components/projects_dropdown_filter.vue';
import RecentActivityCard from 'ee/analytics/cycle_analytics/components/recent_activity_card.vue'; import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue';
import TimeMetricsCard from 'ee/analytics/cycle_analytics/components/time_metrics_card.vue';
import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigation.vue'; import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigation.vue';
import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue'; import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue';
import StageTableNav from 'ee/analytics/cycle_analytics/components/stage_table_nav.vue'; import StageTableNav from 'ee/analytics/cycle_analytics/components/stage_table_nav.vue';
...@@ -38,7 +37,6 @@ const localVue = createLocalVue(); ...@@ -38,7 +37,6 @@ const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
const defaultStubs = { const defaultStubs = {
'recent-activity-card': true,
'stage-event-list': true, 'stage-event-list': true,
'stage-nav-item': true, 'stage-nav-item': true,
'tasks-by-type-chart': true, 'tasks-by-type-chart': true,
...@@ -46,6 +44,7 @@ const defaultStubs = { ...@@ -46,6 +44,7 @@ const defaultStubs = {
DurationChart: true, DurationChart: true,
GroupsDropdownFilter: true, GroupsDropdownFilter: true,
ValueStreamSelect: true, ValueStreamSelect: true,
Metrics: true,
}; };
const defaultFeatureFlags = { const defaultFeatureFlags = {
...@@ -150,12 +149,8 @@ describe('Cycle Analytics component', () => { ...@@ -150,12 +149,8 @@ describe('Cycle Analytics component', () => {
expect(wrapper.find(Daterange).exists()).toBe(flag); expect(wrapper.find(Daterange).exists()).toBe(flag);
}; };
const displaysRecentActivityCard = flag => { const displaysMetrics = flag => {
expect(wrapper.find(RecentActivityCard).exists()).toBe(flag); expect(wrapper.contains(Metrics)).toBe(flag);
};
const displaysTimeMetricsCard = flag => {
expect(wrapper.find(TimeMetricsCard).exists()).toBe(flag);
}; };
const displaysStageTable = flag => { const displaysStageTable = flag => {
...@@ -225,12 +220,8 @@ describe('Cycle Analytics component', () => { ...@@ -225,12 +220,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false); displaysDateRangePicker(false);
}); });
it('does not display the recent activity card', () => { it('does not display the metrics cards', () => {
displaysRecentActivityCard(false); displaysMetrics(false);
});
it('does not display the time metrics card', () => {
displaysTimeMetricsCard(false);
}); });
it('does not display the stage table', () => { it('does not display the stage table', () => {
...@@ -335,12 +326,8 @@ describe('Cycle Analytics component', () => { ...@@ -335,12 +326,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(true); displaysDateRangePicker(true);
}); });
it('displays the recent activity card', () => { it('displays the metrics', () => {
displaysRecentActivityCard(true); displaysMetrics(true);
});
it('displays the time metrics card', () => {
displaysTimeMetricsCard(true);
}); });
it('displays the stage table', () => { it('displays the stage table', () => {
...@@ -473,12 +460,8 @@ describe('Cycle Analytics component', () => { ...@@ -473,12 +460,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false); displaysDateRangePicker(false);
}); });
it('does not display the recent activity card', () => { it('does not display the metrics', () => {
displaysRecentActivityCard(false); displaysMetrics(false);
});
it('does not display the time metrics card', () => {
displaysTimeMetricsCard(false);
}); });
it('does not display the stage table', () => { it('does not display the stage table', () => {
......
import { shallowMount } from '@vue/test-utils';
import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue';
import TimeMetricsCard from 'ee/analytics/cycle_analytics/components/time_metrics_card.vue';
import { OVERVIEW_METRICS } from 'ee/analytics/cycle_analytics/constants';
import { group } from '../mock_data';
describe('Metrics', () => {
const { full_path: groupPath } = group;
let wrapper;
const createComponent = ({ requestParams = {} } = {}) => {
return shallowMount(Metrics, {
propsData: {
groupPath,
requestParams,
},
});
};
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
const findTimeMetricsAtIndex = index => wrapper.findAll(TimeMetricsCard).at(index);
it.each`
metric | index | requestType
${'time summary'} | ${0} | ${OVERVIEW_METRICS.TIME_SUMMARY}
${'recent activity'} | ${1} | ${OVERVIEW_METRICS.RECENT_ACTIVITY}
`('renders the $metric', ({ index, requestType }) => {
const card = findTimeMetricsAtIndex(index);
expect(card.props('requestType')).toBe(requestType);
expect(card.html()).toMatchSnapshot();
});
});
import { mount } from '@vue/test-utils';
import RecentActivityCard from 'ee/analytics/cycle_analytics/components/recent_activity_card.vue';
import Api from 'ee/api';
import createFlash from '~/flash';
import { group, recentActivityData } from '../mock_data';
jest.mock('~/flash');
describe('RecentActivityCard', () => {
const { full_path: groupPath } = group;
let wrapper;
const createComponent = (additionalParams = {}) => {
return mount(RecentActivityCard, {
propsData: {
groupPath,
additionalParams,
},
});
};
beforeEach(() => {
jest.spyOn(Api, 'cycleAnalyticsSummaryData').mockResolvedValue({ data: recentActivityData });
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('fetches the recent activity data', () => {
expect(Api.cycleAnalyticsSummaryData).toHaveBeenCalledWith(groupPath, {});
});
describe('with a failing request', () => {
beforeEach(() => {
jest.spyOn(Api, 'cycleAnalyticsSummaryData').mockRejectedValue();
wrapper = createComponent();
});
it('should render an error message', () => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error while fetching value stream analytics recent activity data.',
);
});
});
describe('with additional params', () => {
beforeEach(() => {
wrapper = createComponent({
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
});
});
it('sends additional parameters as query paremeters', () => {
expect(Api.cycleAnalyticsSummaryData).toHaveBeenCalledWith(groupPath, {
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
});
});
});
});
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import TimeMetricsCard from 'ee/analytics/cycle_analytics/components/time_metrics_card.vue'; import TimeMetricsCard from 'ee/analytics/cycle_analytics/components/time_metrics_card.vue';
import { OVERVIEW_METRICS } from 'ee/analytics/cycle_analytics/constants';
import Api from 'ee/api'; import Api from 'ee/api';
import { group, timeMetricsData, recentActivityData } from '../mock_data';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { group, timeMetricsData } from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -10,62 +11,78 @@ describe('TimeMetricsCard', () => { ...@@ -10,62 +11,78 @@ describe('TimeMetricsCard', () => {
const { full_path: groupPath } = group; const { full_path: groupPath } = group;
let wrapper; let wrapper;
const createComponent = ({ additionalParams = {} } = {}) => { const template = `
<div slot-scope="{ metrics }">
<span v-for="metric in metrics">{{metric.value}} {{metric.unit}}</span>
</div>`;
const createComponent = ({ additionalParams = {}, requestType } = {}) => {
return shallowMount(TimeMetricsCard, { return shallowMount(TimeMetricsCard, {
propsData: { propsData: {
groupPath, groupPath,
additionalParams, additionalParams,
requestType,
}, },
slots: { scopedSlots: {
default: 'mockMetricCard', default: template,
}, },
}); });
}; };
beforeEach(() => { describe.each`
jest.spyOn(Api, 'cycleAnalyticsTimeSummaryData').mockResolvedValue({ data: timeMetricsData }); metric | requestType | request | data
${'Recent activity'} | ${OVERVIEW_METRICS.RECENT_ACTIVITY} | ${'cycleAnalyticsSummaryData'} | ${recentActivityData}
wrapper = createComponent(); ${'Time summary'} | ${OVERVIEW_METRICS.TIME_SUMMARY} | ${'cycleAnalyticsTimeSummaryData'} | ${timeMetricsData}
}); `('$metric', ({ requestType, request, data, metric }) => {
beforeEach(() => {
jest.spyOn(Api, request).mockResolvedValue({ data });
wrapper = createComponent({ requestType });
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; wrapper = null;
}); });
it('fetches the time metrics data', () => {
expect(Api.cycleAnalyticsTimeSummaryData).toHaveBeenCalledWith(groupPath, {});
});
describe('with a failing request', () => { it(`renders the ${metric} metric`, () => {
beforeEach(() => { expect(wrapper.html()).toMatchSnapshot();
jest.spyOn(Api, 'cycleAnalyticsTimeSummaryData').mockRejectedValue(); });
wrapper = createComponent(); it('fetches the metric data', () => {
expect(Api[request]).toHaveBeenCalledWith(groupPath, {});
}); });
it('should render an error message', () => { describe('with a failing request', () => {
expect(createFlash).toHaveBeenCalledWith( beforeEach(() => {
'There was an error while fetching value stream analytics time summary data.', jest.spyOn(Api, request).mockRejectedValue();
); wrapper = createComponent({ requestType });
});
it('should render an error message', () => {
expect(createFlash).toHaveBeenCalledWith(
`There was an error while fetching value stream analytics ${metric.toLowerCase()} data.`,
);
});
}); });
});
describe('with additional params', () => { describe('with additional params', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
additionalParams: { requestType,
additionalParams: {
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
},
});
});
it('sends additional parameters as query paremeters', () => {
expect(Api[request]).toHaveBeenCalledWith(groupPath, {
'project_ids[]': [1], 'project_ids[]': [1],
created_after: '2020-01-01', created_after: '2020-01-01',
created_before: '2020-02-01', created_before: '2020-02-01',
}, });
});
});
it('sends additional parameters as query paremeters', () => {
expect(Api.cycleAnalyticsTimeSummaryData).toHaveBeenCalledWith(groupPath, {
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
}); });
}); });
}); });
......
...@@ -24830,6 +24830,9 @@ msgstr "" ...@@ -24830,6 +24830,9 @@ msgstr ""
msgid "There was an error while fetching the table data." msgid "There was an error while fetching the table data."
msgstr "" msgstr ""
msgid "There was an error while fetching value stream analytics %{requestTypeName} data."
msgstr ""
msgid "There was an error while fetching value stream analytics data." msgid "There was an error while fetching value stream analytics data."
msgstr "" msgstr ""
...@@ -24839,12 +24842,6 @@ msgstr "" ...@@ -24839,12 +24842,6 @@ msgstr ""
msgid "There was an error while fetching value stream analytics duration median data." msgid "There was an error while fetching value stream analytics duration median data."
msgstr "" msgstr ""
msgid "There was an error while fetching value stream analytics recent activity data."
msgstr ""
msgid "There was an error while fetching value stream analytics time summary data."
msgstr ""
msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again." msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again."
msgstr "" msgstr ""
...@@ -29728,6 +29725,9 @@ msgstr "" ...@@ -29728,6 +29725,9 @@ msgstr ""
msgid "quick actions" msgid "quick actions"
msgstr "" msgstr ""
msgid "recent activity"
msgstr ""
msgid "register" msgid "register"
msgstr "" msgstr ""
...@@ -29892,6 +29892,9 @@ msgstr "" ...@@ -29892,6 +29892,9 @@ msgstr ""
msgid "this document" msgid "this document"
msgstr "" msgstr ""
msgid "time summary"
msgstr ""
msgid "to help your contributors communicate effectively!" msgid "to help your contributors communicate effectively!"
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