Commit 97077a5b authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Moved url sync to a mixin

Moves url sync from a renderless component
to a mixin, updated base_spec tests

Added specs for url_sync_mixin

Fixed minor rebase conflicts
parent 059a6af4
......@@ -13,7 +13,7 @@ import StageDropdownFilter from './stage_dropdown_filter.vue';
import SummaryTable from './summary_table.vue';
import StageTable from './stage_table.vue';
import TasksByTypeChart from './tasks_by_type_chart.vue';
import UrlSync from './url_sync.vue';
import UrlSyncMixin from '../mixins/url_sync_mixin';
export default {
name: 'CycleAnalytics',
......@@ -28,9 +28,8 @@ export default {
StageDropdownFilter,
Scatterplot,
TasksByTypeChart,
UrlSync,
},
mixins: [glFeatureFlagsMixin()],
mixins: [glFeatureFlagsMixin(), UrlSyncMixin],
props: {
emptyStateSvgPath: {
type: String,
......@@ -202,10 +201,8 @@ export default {
maxDateRange: DATE_RANGE_LIMIT,
};
</script>
<template>
<div class="js-cycle-analytics">
<url-sync />
<div class="page-title-holder d-flex align-items-center">
<h3 class="page-title">{{ __('Value Stream Analytics') }}</h3>
</div>
......
<script>
import { mapState, mapGetters } from 'vuex';
import { historyPushState } from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
......@@ -22,8 +21,4 @@ export default {
historyPushState(setUrlParams(this.query, window.location.href, true));
},
},
render() {
return null;
},
};
</script>
import dateFormat from 'dateformat';
import Api from 'ee/api';
import { getDayDifference, getDateInPast } from '~/lib/utils/datetime_utility';
import createFlash, { hideFlash } from '~/flash';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import httpStatus from '~/lib/utils/http_status';
import * as types from './mutation_types';
import { dateFormats } from '../../shared/constants';
const removeError = () => {
const flashEl = document.querySelector('.flash-alert');
if (flashEl) {
hideFlash(flashEl);
}
};
import { removeFlash } from '../utils';
const handleErrorOrRethrow = ({ action, error }) => {
if (error?.response?.status === httpStatus.FORBIDDEN) {
......@@ -32,9 +26,7 @@ const isStageNameExistsError = ({ status, errors }) => {
export const setFeatureFlags = ({ commit }, featureFlags) =>
commit(types.SET_FEATURE_FLAGS, featureFlags);
export const setSelectedGroup = ({ commit }, group) => {
commit(types.SET_SELECTED_GROUP, group);
};
export const setSelectedGroup = ({ commit }, group) => commit(types.SET_SELECTED_GROUP, group);
export const setSelectedProjects = ({ commit }, projects) => {
commit(types.SET_SELECTED_PROJECTS, projects);
......
......@@ -21,11 +21,14 @@ import * as commonUtils from '~/lib/utils/common_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import { toYmd } from 'ee/analytics/shared/utils';
import * as mockData from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import UrlSyncMixin from 'ee/analytics/cycle_analytics/mixins/url_sync_mixin';
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const emptyStateSvgPath = 'path/to/empty/state';
const hideGroupDropDown = false;
const selectedGroup = convertObjectPropsToCamelCase(mockData.group);
const localVue = createLocalVue();
localVue.use(Vuex);
......@@ -52,6 +55,7 @@ function createComponent({
const comp = func(Component, {
localVue,
store,
mixins: [UrlSyncMixin],
propsData: {
emptyStateSvgPath,
noDataSvgPath,
......@@ -71,14 +75,13 @@ function createComponent({
});
comp.vm.$store.dispatch('initializeCycleAnalytics', {
group: mockData.group,
createdAfter: mockData.startDate,
createdBefore: mockData.endDate,
});
if (withStageSelected) {
comp.vm.$store.dispatch('setSelectedGroup', {
...mockData.group,
...selectedGroup,
});
comp.vm.$store.dispatch('receiveGroupStagesAndEventsSuccess', {
......@@ -144,6 +147,7 @@ describe('Cycle Analytics component', () => {
afterEach(() => {
wrapper.destroy();
mock.restore();
wrapper = null;
});
describe('displays the components as required', () => {
......@@ -360,9 +364,6 @@ describe('Cycle Analytics component', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createComponent({
opts: {
stubs: defaultStubs,
},
shallow: false,
withStageSelected: true,
customizableCycleAnalyticsEnabled: true,
......@@ -384,9 +385,6 @@ describe('Cycle Analytics component', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createComponent({
opts: {
stubs: defaultStubs,
},
shallow: false,
withStageSelected: true,
customizableCycleAnalyticsEnabled: false,
......@@ -408,9 +406,6 @@ describe('Cycle Analytics component', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createComponent({
opts: {
stubs: defaultStubs,
},
shallow: false,
withStageSelected: true,
customizableCycleAnalyticsEnabled: false,
......@@ -619,28 +614,21 @@ describe('Cycle Analytics component', () => {
urlUtils.setUrlParams = jest.fn();
mock = new MockAdapter(axios);
wrapper = createComponent({
shallow: false,
scatterplotEnabled: false,
tasksByTypeChartEnabled: false,
stubs: {
...defaultStubs,
},
});
wrapper.vm.$store.dispatch('initializeCycleAnalytics', {
createdAfter: mockData.startDate,
createdBefore: mockData.endDate,
});
});
afterEach(() => {
commonUtils.historyPushState = null;
urlUtils.setUrlParams = null;
wrapper.destroy();
mock.restore();
return wrapper.vm.$nextTick();
});
it('sets the created_after and created_before url parameters', () => {
shouldSetUrlParams({
return shouldSetUrlParams({
created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate),
group_id: null,
......@@ -649,19 +637,25 @@ describe('Cycle Analytics component', () => {
});
describe('with a group selected', () => {
const fakeGroup = {
id: 2,
path: 'new-test',
fullPath: 'new-test-group',
name: 'New test group',
};
beforeEach(() => {
wrapper.vm.$store.dispatch('setSelectedGroup', {
...mockData.group,
...fakeGroup,
});
return wrapper.vm.$nextTick();
});
it('sets the group_id url parameter', () => {
shouldSetUrlParams({
return shouldSetUrlParams({
created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate),
group_id: mockData.group.full_path,
group_id: fakeGroup.fullPath,
'project_ids[]': [],
});
});
......@@ -672,7 +666,7 @@ describe('Cycle Analytics component', () => {
beforeEach(() => {
wrapper.vm.$store.dispatch('setSelectedGroup', {
...mockData.group,
...selectedGroup,
});
wrapper.vm.$store.dispatch('setSelectedProjects', mockData.selectedProjects);
......@@ -681,10 +675,10 @@ describe('Cycle Analytics component', () => {
});
it('sets the project_ids url parameter', () => {
shouldSetUrlParams({
return shouldSetUrlParams({
created_after: toYmd(mockData.startDate),
created_before: toYmd(mockData.endDate),
group_id: mockData.group.full_path,
group_id: selectedGroup.fullPath,
'project_ids[]': selectedProjectIds,
});
});
......
import Vue from 'vue';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import store from 'ee/analytics/cycle_analytics/store';
import UrlSyncMixin from 'ee/analytics/cycle_analytics/mixins/url_sync_mixin';
import { toYmd } from 'ee/analytics/shared/utils';
import { startDate, endDate } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
const createComponent = () => {
const Component = Vue.extend({
localVue,
store,
mixins: [UrlSyncMixin],
render(h) {
return h('div');
},
});
return shallowMount(Component);
};
describe('UrlSyncMixin', () => {
let wrapper;
beforeEach(() => {
wrapper = createComponent();
wrapper.vm.$store.dispatch('initializeCycleAnalytics', {
createdAfter: startDate,
createdBefore: endDate,
});
});
afterEach(() => {
wrapper.vm.$destroy();
});
describe('watch', () => {
describe('query', () => {
const defaultState = {
group_id: null,
'project_ids[]': [],
created_after: toYmd(startDate),
created_before: toYmd(endDate),
};
it('sets the start and end date to the default state values', () => {
expect(wrapper.vm.query).toEqual(defaultState);
});
it.each`
param | action | payload | updatedParams
${'group_id'} | ${'setSelectedGroup'} | ${{ fullPath: 'test-group', name: 'test group' }} | ${{ group_id: 'test-group' }}
${'project_ids'} | ${'setSelectedProjects'} | ${[{ id: 1 }, { id: 2 }]} | ${{ 'project_ids[]': [1, 2] }}
${'created_after'} | ${'setDateRange'} | ${{ startDate: '2020-06-18', endDate, skipFetch: true }} | ${{ created_after: toYmd('2020-06-18') }}
${'created_before'} | ${'setDateRange'} | ${{ endDate: '2020-06-18', startDate, skipFetch: true }} | ${{ created_before: toYmd('2020-06-18') }}
`(
'sets the $param parameter when $action is dispatched',
({ action, payload, updatedParams }) => {
wrapper.vm.$store.dispatch(action, payload);
expect(wrapper.vm.query).toEqual({
...defaultState,
...updatedParams,
});
},
);
});
});
});
......@@ -39,10 +39,6 @@ describe('Cycle analytics actions', () => {
let state;
let mock;
function shouldFlashAMessage(msg = flashErrorMessage) {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
}
beforeEach(() => {
state = {
startDate,
......
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