Commit 3504a360 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Kushal Pandya

Display toggle for aggregation

Adds a GlToggle component to allow
users to enable / disable the VSA aggregated
backend.
parent c33c5d65
<script> <script>
import { GlIcon, GlToggle, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import DateRange from '~/analytics/shared/components/daterange.vue'; import DateRange from '~/analytics/shared/components/daterange.vue';
import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue'; import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue';
import { DATE_RANGE_LIMIT, PROJECTS_PER_PAGE } from '~/analytics/shared/constants'; import { DATE_RANGE_LIMIT, PROJECTS_PER_PAGE } from '~/analytics/shared/constants';
import FilterBar from './filter_bar.vue'; import FilterBar from './filter_bar.vue';
export const AGGREGATION_TOGGLE_LABEL = s__('CycleAnalytics|Filter by stop date');
export const AGGREGATION_DESCRIPTION = s__(
'CycleAnalytics|When enabled, the results show items with a stop event within the date range. When disabled, the results show items with a start event within the date range.',
);
export default { export default {
name: 'ValueStreamFilters', name: 'ValueStreamFilters',
components: { components: {
GlIcon,
GlToggle,
DateRange, DateRange,
ProjectsDropdownFilter, ProjectsDropdownFilter,
FilterBar, FilterBar,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
selectedProjects: { selectedProjects: {
type: Array, type: Array,
...@@ -45,6 +57,21 @@ export default { ...@@ -45,6 +57,21 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
canToggleAggregation: {
type: Boolean,
required: false,
default: false,
},
isAggregationEnabled: {
type: Boolean,
required: false,
default: false,
},
isUpdatingAggregationData: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
projectsQueryParams() { projectsQueryParams() {
...@@ -54,8 +81,19 @@ export default { ...@@ -54,8 +81,19 @@ export default {
}; };
}, },
}, },
methods: {
onUpdateAggregation(ev) {
if (!this.isUpdatingAggregationData) {
this.$emit('toggleAggregation', ev);
}
},
},
multiProjectSelect: true, multiProjectSelect: true,
maxDateRange: DATE_RANGE_LIMIT, maxDateRange: DATE_RANGE_LIMIT,
i18n: {
AGGREGATION_TOGGLE_LABEL,
AGGREGATION_DESCRIPTION,
},
}; };
</script> </script>
<template> <template>
...@@ -84,7 +122,28 @@ export default { ...@@ -84,7 +122,28 @@ export default {
@selected="$emit('selectProject', $event)" @selected="$emit('selectProject', $event)"
/> />
</div> </div>
<div> <div class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row">
<div
v-if="canToggleAggregation"
class="gl-display-flex gl-text-align-center gl-my-2 gl-lg-mt-0 gl-lg-mb-0 gl-mr-5"
>
<gl-toggle
class="gl-flex-direction-row"
:value="isAggregationEnabled"
:label="$options.i18n.AGGREGATION_TOGGLE_LABEL"
:disabled="isUpdatingAggregationData"
label-position="left"
@change="onUpdateAggregation"
>
<template #label>
{{ $options.i18n.AGGREGATION_TOGGLE_LABEL }}&nbsp;<gl-icon
v-gl-tooltip.hover
:title="$options.i18n.AGGREGATION_DESCRIPTION"
name="information-o"
/>
</template>
</gl-toggle>
</div>
<date-range <date-range
v-if="hasDateRangeFilter" v-if="hasDateRangeFilter"
:start-date="startDate" :start-date="startDate"
......
<script> <script>
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; import ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue';
...@@ -27,6 +31,7 @@ export default { ...@@ -27,6 +31,7 @@ export default {
ValueStreamSelect, ValueStreamSelect,
UrlSync, UrlSync,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
emptyStateSvgPath: { emptyStateSvgPath: {
type: String, type: String,
...@@ -58,6 +63,7 @@ export default { ...@@ -58,6 +63,7 @@ export default {
'selectedValueStream', 'selectedValueStream',
'pagination', 'pagination',
'aggregation', 'aggregation',
'isUpdatingAggregation',
]), ]),
...mapGetters([ ...mapGetters([
'hasNoAccessError', 'hasNoAccessError',
...@@ -84,8 +90,11 @@ export default { ...@@ -84,8 +90,11 @@ export default {
hasDateRangeSet() { hasDateRangeSet() {
return this.createdAfter && this.createdBefore; return this.createdAfter && this.createdBefore;
}, },
canToggleAggregation() {
return this.glFeatures.useVsaAggregatedTables;
},
isAggregationEnabled() { isAggregationEnabled() {
return this.aggregation?.enabled; return this.canToggleAggregation && this.aggregation?.enabled;
}, },
query() { query() {
const { project_ids, created_after, created_before } = this.cycleAnalyticsRequestParams; const { project_ids, created_after, created_before } = this.cycleAnalyticsRequestParams;
...@@ -123,6 +132,7 @@ export default { ...@@ -123,6 +132,7 @@ export default {
'setDefaultSelectedStage', 'setDefaultSelectedStage',
'setDateRange', 'setDateRange',
'updateStageTablePagination', 'updateStageTablePagination',
'updateAggregation',
]), ]),
onProjectsSelect(projects) { onProjectsSelect(projects) {
this.setSelectedProjects(projects); this.setSelectedProjects(projects);
...@@ -144,6 +154,37 @@ export default { ...@@ -144,6 +154,37 @@ export default {
onHandleUpdatePagination(data) { onHandleUpdatePagination(data) {
this.updateStageTablePagination(data); this.updateStageTablePagination(data);
}, },
onToggleAggregation(value) {
this.updateAggregation(value)
.then(() => {
this.$toast.show(
value
? s__('CycleAnalytics|Aggregation enabled')
: s__('CycleAnalytics|Aggregation disabled'),
);
/*
* NOTE: We have opted for a hard page refresh here as the cleanest way to
* ensure users will be seeing accurate information when this request succeeds, or correctly
* prompted to create a value stream.
*
* With https://gitlab.com/groups/gitlab-org/-/epics/6046 we are changing how we calculate
* data for value stream analytics. One of the side effects will be removing the "in memory"
* default value stream that has currently been available when there are no custom value streams.
*
* All the API requests require at least 1 value stream to exist in the group, if there are no
* value streams available we will instead display an empty state with next steps on how
* to set up your first custom value stream: https://gitlab.com/gitlab-org/gitlab/-/issues/351853.
*/
refreshCurrentPage();
})
.catch(() => {
createFlash({
message: s__(
'CycleAnalytics|There was an error updating the aggregation status, please try again.',
),
});
});
},
}, },
METRICS_REQUESTS, METRICS_REQUESTS,
aggregationPopoverOptions: { aggregationPopoverOptions: {
...@@ -187,6 +228,10 @@ export default { ...@@ -187,6 +228,10 @@ export default {
:selected-projects="selectedProjects" :selected-projects="selectedProjects"
:start-date="createdAfter" :start-date="createdAfter"
:end-date="createdBefore" :end-date="createdBefore"
:can-toggle-aggregation="canToggleAggregation"
:is-aggregation-enabled="isAggregationEnabled"
:is-updating-aggregation-data="isLoading || isUpdatingAggregation"
@toggleAggregation="onToggleAggregation"
@selectProject="onProjectsSelect" @selectProject="onProjectsSelect"
@setDateRange="onSetDateRange" @setDateRange="onSetDateRange"
/> />
......
...@@ -2,6 +2,18 @@ import Api from 'ee/api'; ...@@ -2,6 +2,18 @@ import Api from 'ee/api';
import { FETCH_VALUE_STREAM_DATA } from '../../constants'; import { FETCH_VALUE_STREAM_DATA } from '../../constants';
import * as types from '../mutation_types'; import * as types from '../mutation_types';
export const updateAggregation = ({ commit, getters }, status) => {
const { currentGroupPath } = getters;
commit(types.REQUEST_UPDATE_AGGREGATION);
return Api.cycleAnalyticsUpdateAggregation(currentGroupPath, { enabled: status })
.then(() => commit(types.RECEIVE_UPDATE_AGGREGATION_SUCCESS))
.catch((err) => {
commit(types.RECEIVE_UPDATE_AGGREGATION_ERROR);
throw err;
});
};
export const receiveCreateValueStreamSuccess = ({ commit, dispatch }, valueStream = {}) => { export const receiveCreateValueStreamSuccess = ({ commit, dispatch }, valueStream = {}) => {
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream); commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream);
return dispatch('fetchCycleAnalyticsData'); return dispatch('fetchCycleAnalyticsData');
......
...@@ -49,3 +49,7 @@ export const RECEIVE_DELETE_VALUE_STREAM_ERROR = 'RECEIVE_DELETE_VALUE_STREAM_ER ...@@ -49,3 +49,7 @@ export const RECEIVE_DELETE_VALUE_STREAM_ERROR = 'RECEIVE_DELETE_VALUE_STREAM_ER
export const REQUEST_VALUE_STREAMS = 'REQUEST_VALUE_STREAMS'; export const REQUEST_VALUE_STREAMS = 'REQUEST_VALUE_STREAMS';
export const RECEIVE_VALUE_STREAMS_SUCCESS = 'RECEIVE_VALUE_STREAMS_SUCCESS'; export const RECEIVE_VALUE_STREAMS_SUCCESS = 'RECEIVE_VALUE_STREAMS_SUCCESS';
export const RECEIVE_VALUE_STREAMS_ERROR = 'RECEIVE_VALUE_STREAMS_ERROR'; export const RECEIVE_VALUE_STREAMS_ERROR = 'RECEIVE_VALUE_STREAMS_ERROR';
export const REQUEST_UPDATE_AGGREGATION = 'REQUEST_UPDATE_AGGREGATION';
export const RECEIVE_UPDATE_AGGREGATION_SUCCESS = 'RECEIVE_UPDATE_AGGREGATION_SUCCESS';
export const RECEIVE_UPDATE_AGGREGATION_ERROR = 'RECEIVE_UPDATE_AGGREGATION_ERROR';
...@@ -200,4 +200,13 @@ export default { ...@@ -200,4 +200,13 @@ export default {
direction: direction || PAGINATION_SORT_DIRECTION_DESC, direction: direction || PAGINATION_SORT_DIRECTION_DESC,
}); });
}, },
[types.REQUEST_UPDATE_AGGREGATION](state) {
state.isUpdatingAggregation = true;
},
[types.RECEIVE_UPDATE_AGGREGATION_SUCCESS](state) {
state.isUpdatingAggregation = true;
},
[types.RECEIVE_UPDATE_AGGREGATION_ERROR](state) {
state.isUpdatingAggregation = false;
},
}; };
...@@ -28,6 +28,7 @@ export default () => ({ ...@@ -28,6 +28,7 @@ export default () => ({
isEditingValueStream: false, isEditingValueStream: false,
isDeletingValueStream: false, isDeletingValueStream: false,
isFetchingGroupLabels: false, isFetchingGroupLabels: false,
isUpdatingAggregation: false,
createValueStreamErrors: {}, createValueStreamErrors: {},
deleteValueStreamError: null, deleteValueStreamError: null,
......
...@@ -23,6 +23,8 @@ export default { ...@@ -23,6 +23,8 @@ export default {
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',
cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json', cycleAnalyticsGroupLabelsPath: '/groups/:namespace_path/-/labels.json',
cycleAnalyticsAggregationPath:
'/groups/:namespace_path/-/analytics/value_stream_analytics/use_aggregated_backend',
codeReviewAnalyticsPath: '/api/:version/analytics/code_review', codeReviewAnalyticsPath: '/api/:version/analytics/code_review',
groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count', groupActivityIssuesPath: '/api/:version/analytics/group_activity/issues_count',
groupActivityMergeRequestsPath: '/api/:version/analytics/group_activity/merge_requests_count', groupActivityMergeRequestsPath: '/api/:version/analytics/group_activity/merge_requests_count',
...@@ -209,6 +211,14 @@ export default { ...@@ -209,6 +211,14 @@ export default {
}); });
}, },
cycleAnalyticsUpdateAggregation(groupId, data) {
const url = Api.buildUrl(this.cycleAnalyticsAggregationPath).replace(
':namespace_path',
groupId,
);
return axios.put(url, data);
},
codeReviewAnalytics(params = {}) { codeReviewAnalytics(params = {}) {
const url = Api.buildUrl(this.codeReviewAnalyticsPath); const url = Api.buildUrl(this.codeReviewAnalyticsPath);
return axios.get(url, { params }); return axios.get(url, { params });
......
...@@ -16,6 +16,10 @@ class Groups::Analytics::CycleAnalyticsController < Groups::Analytics::Applicati ...@@ -16,6 +16,10 @@ class Groups::Analytics::CycleAnalyticsController < Groups::Analytics::Applicati
render_403 unless can?(current_user, :read_group_cycle_analytics, @group) render_403 unless can?(current_user, :read_group_cycle_analytics, @group)
end end
before_action do
push_frontend_feature_flag(:use_vsa_aggregated_tables, @group, default_enabled: :yaml)
end
layout 'group' layout 'group'
track_redis_hll_event :show, name: 'g_analytics_valuestream' track_redis_hll_event :show, name: 'g_analytics_valuestream'
......
...@@ -141,6 +141,12 @@ describe('EE Value Stream Analytics component', () => { ...@@ -141,6 +141,12 @@ describe('EE Value Stream Analytics component', () => {
noAccessSvgPath, noAccessSvgPath,
...props, ...props,
}, },
provide: {
glFeatures: {
useVsaAggregatedTables: true,
...featureFlags,
},
},
mocks, mocks,
...opts, ...opts,
}); });
...@@ -374,6 +380,26 @@ describe('EE Value Stream Analytics component', () => { ...@@ -374,6 +380,26 @@ describe('EE Value Stream Analytics component', () => {
expect(findAggregationStatus().exists()).toBe(false); expect(findAggregationStatus().exists()).toBe(false);
}); });
}); });
describe('useVsaAggregatedTables = false', () => {
beforeEach(async () => {
wrapper = await createComponent({
initialState: {
...initialCycleAnalyticsState,
aggregation: {
...aggregationData,
},
},
featureFlags: {
useVsaAggregatedTables: false,
},
});
});
it('does not render the aggregation status', () => {
expect(findAggregationStatus().exists()).toBe(false);
});
});
}); });
describe('with failed requests while loading', () => { describe('with failed requests while loading', () => {
......
...@@ -53,6 +53,7 @@ export const endpoints = { ...@@ -53,6 +53,7 @@ export const endpoints = {
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/,
valueStreamData: /analytics\/value_stream_analytics\/value_streams/, valueStreamData: /analytics\/value_stream_analytics\/value_streams/,
valueStreamAggregationData: /analytics\/value_stream_analytics\/use_aggregated_backend/,
}; };
export const valueStreams = [ export const valueStreams = [
......
...@@ -382,4 +382,48 @@ describe('Value Stream Analytics actions / value streams', () => { ...@@ -382,4 +382,48 @@ describe('Value Stream Analytics actions / value streams', () => {
); );
}); });
}); });
describe('updateAggregation', () => {
beforeEach(() => {
state = { currentGroup, aggregation: { enabled: false } };
});
describe('with no errors', () => {
beforeEach(() => {
mock
.onPut(endpoints.valueStreamAggregationData)
.replyOnce(httpStatusCodes.OK, { enabled: true });
});
it(`commits the ${types.REQUEST_UPDATE_AGGREGATION} and ${types.RECEIVE_UPDATE_AGGREGATION_SUCCESS} actions`, () => {
return testAction(actions.updateAggregation, true, state, [
{ type: types.REQUEST_UPDATE_AGGREGATION },
{ type: types.RECEIVE_UPDATE_AGGREGATION_SUCCESS },
]);
});
});
describe('with a failing request', () => {
let mockCommit;
beforeEach(() => {
mockCommit = jest.fn();
mock.onGet(endpoints.valueStreamAggregationData).reply(httpStatusCodes.NOT_FOUND);
});
it(`will commit ${types.RECEIVE_VALUE_STREAMS_ERROR}`, () => {
return actions.updateAggregation({ state, getters, commit: mockCommit }).catch(() => {
expect(mockCommit.mock.calls).toEqual([
['REQUEST_UPDATE_AGGREGATION'],
['RECEIVE_UPDATE_AGGREGATION_ERROR'],
]);
});
});
it('throws an error', () => {
return expect(
actions.updateAggregation({ state, getters, commit: mockCommit }),
).rejects.toThrow('Request failed with status code 404');
});
});
});
}); });
...@@ -32,6 +32,9 @@ describe('Value Stream Analytics mutations', () => { ...@@ -32,6 +32,9 @@ describe('Value Stream Analytics mutations', () => {
it.each` it.each`
mutation | stateKey | value mutation | stateKey | value
${types.REQUEST_UPDATE_AGGREGATION} | ${'isUpdatingAggregation'} | ${true}
${types.RECEIVE_UPDATE_AGGREGATION_ERROR} | ${'isUpdatingAggregation'} | ${false}
${types.RECEIVE_UPDATE_AGGREGATION_SUCCESS} | ${'isUpdatingAggregation'} | ${true}
${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]} ${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]} ${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
${types.REQUEST_VALUE_STREAMS} | ${'isLoadingValueStreams'} | ${true} ${types.REQUEST_VALUE_STREAMS} | ${'isLoadingValueStreams'} | ${true}
......
...@@ -447,6 +447,23 @@ describe('Api', () => { ...@@ -447,6 +447,23 @@ describe('Api', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('cycleAnalyticsUpdateAggregation', () => {
it('updates the aggregation enabled status', (done) => {
const reqdata = { enabled: true };
const expectedUrl = `${dummyValueStreamAnalyticsUrlRoot}/use_aggregated_backend`;
mock.onPut(expectedUrl).reply(httpStatus.OK, reqdata);
Api.cycleAnalyticsUpdateAggregation(groupId, reqdata)
.then(({ data, config: { url } }) => {
expect(data).toEqual(reqdata);
expect(url).toEqual(expectedUrl);
})
.then(done)
.catch(done.fail);
});
});
}); });
describe('GroupActivityAnalytics', () => { describe('GroupActivityAnalytics', () => {
......
...@@ -10923,6 +10923,12 @@ msgstr "" ...@@ -10923,6 +10923,12 @@ msgstr ""
msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)" msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)"
msgstr "" msgstr ""
msgid "CycleAnalytics|Aggregation disabled"
msgstr ""
msgid "CycleAnalytics|Aggregation enabled"
msgstr ""
msgid "CycleAnalytics|Average time to completion" msgid "CycleAnalytics|Average time to completion"
msgstr "" msgstr ""
...@@ -10941,6 +10947,9 @@ msgstr "" ...@@ -10941,6 +10947,9 @@ msgstr ""
msgid "CycleAnalytics|Display chart filters" msgid "CycleAnalytics|Display chart filters"
msgstr "" msgstr ""
msgid "CycleAnalytics|Filter by stop date"
msgstr ""
msgid "CycleAnalytics|Lead Time for Changes" msgid "CycleAnalytics|Lead Time for Changes"
msgstr "" msgstr ""
...@@ -10993,12 +11002,18 @@ msgstr "" ...@@ -10993,12 +11002,18 @@ msgstr ""
msgid "CycleAnalytics|There is no data for 'Total time' available. Adjust the current filters." msgid "CycleAnalytics|There is no data for 'Total time' available. Adjust the current filters."
msgstr "" msgstr ""
msgid "CycleAnalytics|There was an error updating the aggregation status, please try again."
msgstr ""
msgid "CycleAnalytics|Total time" msgid "CycleAnalytics|Total time"
msgstr "" msgstr ""
msgid "CycleAnalytics|Type of work" msgid "CycleAnalytics|Type of work"
msgstr "" msgstr ""
msgid "CycleAnalytics|When enabled, the results show items with a stop event within the date range. When disabled, the results show items with a start event within the date range."
msgstr ""
msgid "CycleAnalytics|group dropdown filter" msgid "CycleAnalytics|group dropdown filter"
msgstr "" msgstr ""
......
...@@ -143,9 +143,12 @@ describe('Value stream analytics component', () => { ...@@ -143,9 +143,12 @@ describe('Value stream analytics component', () => {
expect(findFilters().props()).toEqual({ expect(findFilters().props()).toEqual({
groupId, groupId,
groupPath, groupPath,
canToggleAggregation: false,
endDate: createdBefore, endDate: createdBefore,
hasDateRangeFilter: true, hasDateRangeFilter: true,
hasProjectFilter: false, hasProjectFilter: false,
isAggregationEnabled: false,
isUpdatingAggregationData: false,
selectedProjects: [], selectedProjects: [],
startDate: createdAfter, startDate: createdAfter,
}); });
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlToggle } from '@gitlab/ui';
import Daterange from '~/analytics/shared/components/daterange.vue'; import Daterange from '~/analytics/shared/components/daterange.vue';
import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue'; import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue';
import FilterBar from '~/cycle_analytics/components/filter_bar.vue'; import FilterBar from '~/cycle_analytics/components/filter_bar.vue';
...@@ -29,6 +30,7 @@ describe('ValueStreamFilters', () => { ...@@ -29,6 +30,7 @@ describe('ValueStreamFilters', () => {
const findProjectsDropdown = () => wrapper.findComponent(ProjectsDropdownFilter); const findProjectsDropdown = () => wrapper.findComponent(ProjectsDropdownFilter);
const findDateRangePicker = () => wrapper.findComponent(Daterange); const findDateRangePicker = () => wrapper.findComponent(Daterange);
const findFilterBar = () => wrapper.findComponent(FilterBar); const findFilterBar = () => wrapper.findComponent(FilterBar);
const findAggregationToggle = () => wrapper.findComponent(GlToggle);
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
...@@ -57,6 +59,10 @@ describe('ValueStreamFilters', () => { ...@@ -57,6 +59,10 @@ describe('ValueStreamFilters', () => {
expect(findDateRangePicker().exists()).toBe(true); expect(findDateRangePicker().exists()).toBe(true);
}); });
it('will not render the aggregation toggle', () => {
expect(findAggregationToggle().exists()).toBe(false);
});
it('will emit `selectProject` when a project is selected', () => { it('will emit `selectProject` when a project is selected', () => {
findProjectsDropdown().vm.$emit('selected'); findProjectsDropdown().vm.$emit('selected');
...@@ -88,4 +94,52 @@ describe('ValueStreamFilters', () => { ...@@ -88,4 +94,52 @@ describe('ValueStreamFilters', () => {
expect(findProjectsDropdown().exists()).toBe(false); expect(findProjectsDropdown().exists()).toBe(false);
}); });
}); });
describe('canToggleAggregation = true', () => {
beforeEach(() => {
wrapper = createComponent({ isAggregationEnabled: false, canToggleAggregation: true });
});
it('will render the aggregation toggle', () => {
expect(findAggregationToggle().exists()).toBe(true);
});
it('will set the aggregation toggle to the `isAggregationEnabled` value', () => {
expect(findAggregationToggle().props('value')).toBe(false);
wrapper = createComponent({
isAggregationEnabled: true,
canToggleAggregation: true,
});
expect(findAggregationToggle().props('value')).toBe(true);
});
it('will emit `toggleAggregation` when the toggle is changed', async () => {
expect(wrapper.emitted('toggleAggregation')).toBeUndefined();
await findAggregationToggle().vm.$emit('change', true);
expect(wrapper.emitted('toggleAggregation')).toHaveLength(1);
expect(wrapper.emitted('toggleAggregation')).toEqual([[true]]);
});
});
describe('isUpdatingAggregationData = true', () => {
beforeEach(() => {
wrapper = createComponent({ canToggleAggregation: true, isUpdatingAggregationData: true });
});
it('will disable the aggregation toggle', () => {
expect(findAggregationToggle().props('disabled')).toBe(true);
});
it('will not emit `toggleAggregation` when the toggle is changed', async () => {
expect(wrapper.emitted('toggleAggregation')).toBeUndefined();
await findAggregationToggle().vm.$emit('change', true);
expect(wrapper.emitted('toggleAggregation')).toBeUndefined();
});
});
}); });
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