Commit 548da9fb authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Kushal Pandya

Remove filter_mixins from vsa

The filter mixins are no longer used so
we can remove them
parent 3b69d66f
......@@ -2,7 +2,6 @@ import $ from 'jquery';
import Vue from 'vue';
import Cookies from 'js-cookie';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import filterMixins from 'ee_else_ce/analytics/cycle_analytics/mixins/filter_mixins';
import Flash from '../flash';
import { __ } from '~/locale';
import Translate from '../vue_shared/translate';
......@@ -45,7 +44,6 @@ export default () => {
import('ee_component/analytics/shared/components/date_range_dropdown.vue'),
'stage-nav-item': stageNavItem,
},
mixins: [filterMixins],
data() {
return {
store: CycleAnalyticsStore,
......
......@@ -2,7 +2,7 @@
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
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 ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue';
import { SIMILARITY_ORDER, LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants';
......@@ -12,14 +12,12 @@ import DurationChart from './duration_chart.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue';
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 CustomStageForm from './custom_stage_form.vue';
import PathNavigation from './path_navigation.vue';
import MetricCard from '../../shared/components/metric_card.vue';
import FilterBar from './filter_bar.vue';
import ValueStreamSelect from './value_stream_select.vue';
import Metrics from './metrics.vue';
export default {
name: 'CycleAnalytics',
......@@ -32,15 +30,13 @@ export default {
ProjectsDropdownFilter,
StageTable,
TypeOfWorkCharts,
RecentActivityCard,
TimeMetricsCard,
CustomStageForm,
StageTableNav,
PathNavigation,
MetricCard,
FilterBar,
ValueStreamSelect,
UrlSync,
Metrics,
},
props: {
emptyStateSvgPath: {
......@@ -215,7 +211,6 @@ export default {
min_access_level: featureAccessLevel.EVERYONE,
},
maxDateRange: DATE_RANGE_LIMIT,
STAGE_ACTIONS,
};
</script>
<template>
......@@ -304,23 +299,7 @@ export default {
"
/>
<div v-else-if="!errorCode">
<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="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>
<metrics :group-path="currentGroupPath" :request-params="cycleAnalyticsRequestParams" />
<div v-if="isLoading">
<gl-loading-icon class="mt-4" size="md" />
</div>
......@@ -356,7 +335,7 @@ export default {
:events="formEvents"
@createStage="onCreateCustomStage"
@updateStage="onUpdateCustomStage"
@clearErrors="$emit('clearFormErrors')"
@clearErrors="$emit('clear-form-errors')"
/>
</template>
</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>
import Api from 'ee/api';
import { __, s__ } from '~/locale';
import { sprintf, __, s__ } from '~/locale';
import createFlash from '~/flash';
import MetricCard from '../../shared/components/metric_card.vue';
import { removeFlash, prepareTimeMetricsData } from '../utils';
import { OVERVIEW_METRICS } from '../constants';
const I18N_TEXT = {
'lead-time': s__('ValueStreamAnalytics|Median time from issue created 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 {
name: 'TimeMetricsCard',
components: {
......@@ -25,6 +32,11 @@ export default {
required: false,
default: () => ({}),
},
requestType: {
type: String,
required: true,
validator: t => OVERVIEW_METRICS[t],
},
},
data() {
return {
......@@ -44,13 +56,22 @@ export default {
fetchData() {
removeFlash();
this.loading = true;
return Api.cycleAnalyticsTimeSummaryData(this.groupPath, this.additionalParams)
return requestData(this)
.then(({ data }) => {
this.data = prepareTimeMetricsData(data, I18N_TEXT);
})
.catch(() => {
const requestTypeName =
this.requestType === OVERVIEW_METRICS.TIME_SUMMARY
? __('time summary')
: __('recent activity');
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(() => {
......
......@@ -78,3 +78,8 @@ export const CAPITALIZED_STAGE_NAME = Object.keys(STAGE_NAME).reduce((acc, stage
export const PATH_HOME_ICON = 'home';
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';
import MockAdapter from 'axios-mock-adapter';
import GroupsDropdownFilter from 'ee/analytics/shared/components/groups_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 TimeMetricsCard from 'ee/analytics/cycle_analytics/components/time_metrics_card.vue';
import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue';
import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigation.vue';
import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue';
import StageTableNav from 'ee/analytics/cycle_analytics/components/stage_table_nav.vue';
......@@ -38,7 +37,6 @@ const localVue = createLocalVue();
localVue.use(Vuex);
const defaultStubs = {
'recent-activity-card': true,
'stage-event-list': true,
'stage-nav-item': true,
'tasks-by-type-chart': true,
......@@ -46,6 +44,7 @@ const defaultStubs = {
DurationChart: true,
GroupsDropdownFilter: true,
ValueStreamSelect: true,
Metrics: true,
};
const defaultFeatureFlags = {
......@@ -150,12 +149,8 @@ describe('Cycle Analytics component', () => {
expect(wrapper.find(Daterange).exists()).toBe(flag);
};
const displaysRecentActivityCard = flag => {
expect(wrapper.find(RecentActivityCard).exists()).toBe(flag);
};
const displaysTimeMetricsCard = flag => {
expect(wrapper.find(TimeMetricsCard).exists()).toBe(flag);
const displaysMetrics = flag => {
expect(wrapper.contains(Metrics)).toBe(flag);
};
const displaysStageTable = flag => {
......@@ -225,12 +220,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false);
});
it('does not display the recent activity card', () => {
displaysRecentActivityCard(false);
});
it('does not display the time metrics card', () => {
displaysTimeMetricsCard(false);
it('does not display the metrics cards', () => {
displaysMetrics(false);
});
it('does not display the stage table', () => {
......@@ -335,12 +326,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(true);
});
it('displays the recent activity card', () => {
displaysRecentActivityCard(true);
});
it('displays the time metrics card', () => {
displaysTimeMetricsCard(true);
it('displays the metrics', () => {
displaysMetrics(true);
});
it('displays the stage table', () => {
......@@ -473,12 +460,8 @@ describe('Cycle Analytics component', () => {
displaysDateRangePicker(false);
});
it('does not display the recent activity card', () => {
displaysRecentActivityCard(false);
});
it('does not display the time metrics card', () => {
displaysTimeMetricsCard(false);
it('does not display the metrics', () => {
displaysMetrics(false);
});
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 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 { group, timeMetricsData, recentActivityData } from '../mock_data';
import createFlash from '~/flash';
import { group, timeMetricsData } from '../mock_data';
jest.mock('~/flash');
......@@ -10,62 +11,78 @@ describe('TimeMetricsCard', () => {
const { full_path: groupPath } = group;
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, {
propsData: {
groupPath,
additionalParams,
requestType,
},
slots: {
default: 'mockMetricCard',
scopedSlots: {
default: template,
},
});
};
beforeEach(() => {
jest.spyOn(Api, 'cycleAnalyticsTimeSummaryData').mockResolvedValue({ data: timeMetricsData });
wrapper = createComponent();
});
describe.each`
metric | requestType | request | data
${'Recent activity'} | ${OVERVIEW_METRICS.RECENT_ACTIVITY} | ${'cycleAnalyticsSummaryData'} | ${recentActivityData}
${'Time summary'} | ${OVERVIEW_METRICS.TIME_SUMMARY} | ${'cycleAnalyticsTimeSummaryData'} | ${timeMetricsData}
`('$metric', ({ requestType, request, data, metric }) => {
beforeEach(() => {
jest.spyOn(Api, request).mockResolvedValue({ data });
wrapper = createComponent({ requestType });
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('fetches the time metrics data', () => {
expect(Api.cycleAnalyticsTimeSummaryData).toHaveBeenCalledWith(groupPath, {});
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('with a failing request', () => {
beforeEach(() => {
jest.spyOn(Api, 'cycleAnalyticsTimeSummaryData').mockRejectedValue();
it(`renders the ${metric} metric`, () => {
expect(wrapper.html()).toMatchSnapshot();
});
wrapper = createComponent();
it('fetches the metric data', () => {
expect(Api[request]).toHaveBeenCalledWith(groupPath, {});
});
it('should render an error message', () => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error while fetching value stream analytics time summary data.',
);
describe('with a failing request', () => {
beforeEach(() => {
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', () => {
beforeEach(() => {
wrapper = createComponent({
additionalParams: {
describe('with additional params', () => {
beforeEach(() => {
wrapper = createComponent({
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],
created_after: '2020-01-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 ""
msgid "There was an error while fetching the table data."
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."
msgstr ""
......@@ -24839,12 +24842,6 @@ msgstr ""
msgid "There was an error while fetching value stream analytics duration median data."
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."
msgstr ""
......@@ -29728,6 +29725,9 @@ msgstr ""
msgid "quick actions"
msgstr ""
msgid "recent activity"
msgstr ""
msgid "register"
msgstr ""
......@@ -29892,6 +29892,9 @@ msgstr ""
msgid "this document"
msgstr ""
msgid "time summary"
msgstr ""
msgid "to help your contributors communicate effectively!"
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