gitlab-org--gitlab-foss/spec/frontend/cycle_analytics/base_spec.js

279 lines
8.1 KiB
JavaScript

import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BaseComponent from '~/cycle_analytics/components/base.vue';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue';
import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
import initState from '~/cycle_analytics/store/state';
import {
permissions,
transformedProjectStagePathData,
selectedStage,
issueEvents,
createdBefore,
createdAfter,
currentGroup,
stageCounts,
} from './mock_data';
const selectedStageEvents = issueEvents.events;
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const selectedStageCount = stageCounts[selectedStage.id];
const fullPath = 'full/path/to/foo';
Vue.use(Vuex);
let wrapper;
const { id: groupId, path: groupPath } = currentGroup;
const defaultState = {
permissions,
currentGroup,
createdBefore,
createdAfter,
stageCounts,
endpoints: { fullPath, groupId, groupPath },
};
function createStore({ initialState = {}, initialGetters = {} }) {
return new Vuex.Store({
state: {
...initState(),
...defaultState,
...initialState,
},
getters: {
pathNavigationData: () => transformedProjectStagePathData,
filterParams: () => ({
created_after: createdAfter,
created_before: createdBefore,
}),
...initialGetters,
},
});
}
function createComponent({ initialState, initialGetters } = {}) {
return extendedWrapper(
shallowMount(BaseComponent, {
store: createStore({ initialState, initialGetters }),
propsData: {
noDataSvgPath,
noAccessSvgPath,
},
stubs: {
StageTable,
},
}),
);
}
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPathNavigation = () => wrapper.findComponent(PathNavigation);
const findFilters = () => wrapper.findComponent(ValueStreamFilters);
const findOverviewMetrics = () => wrapper.findComponent(ValueStreamMetrics);
const findStageTable = () => wrapper.findComponent(StageTable);
const findStageEvents = () => findStageTable().props('stageEvents');
const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title');
const hasMetricsRequests = (reqs) => {
const foundReqs = findOverviewMetrics().props('requests');
expect(foundReqs.length).toEqual(reqs.length);
expect(foundReqs.map(({ name }) => name)).toEqual(reqs);
};
describe('Value stream analytics component', () => {
beforeEach(() => {
wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents } });
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders the path navigation component', () => {
expect(findPathNavigation().exists()).toBe(true);
});
it('receives the stages formatted for the path navigation', () => {
expect(findPathNavigation().props('stages')).toBe(transformedProjectStagePathData);
});
it('renders the overview metrics', () => {
expect(findOverviewMetrics().exists()).toBe(true);
});
it('passes requests prop to the metrics component', () => {
hasMetricsRequests(['recent activity']);
});
it('renders the stage table', () => {
expect(findStageTable().exists()).toBe(true);
});
it('passes the selected stage count to the stage table', () => {
expect(findStageTable().props('stageCount')).toBe(selectedStageCount);
});
it('renders the stage table events', () => {
expect(findStageEvents()).toEqual(selectedStageEvents);
});
it('renders the filters', () => {
expect(findFilters().exists()).toBe(true);
});
it('displays the date range selector and hides the project selector', () => {
expect(findFilters().props()).toMatchObject({
hasProjectFilter: false,
hasDateRangeFilter: true,
});
});
it('passes the paths to the filter bar', () => {
expect(findFilters().props()).toEqual({
groupId,
groupPath,
endDate: createdBefore,
hasDateRangeFilter: true,
hasProjectFilter: false,
selectedProjects: [],
startDate: createdAfter,
});
});
it('does not render the loading icon', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
describe('with `cycleAnalyticsForGroups=true` license', () => {
beforeEach(() => {
wrapper = createComponent({ initialState: { features: { cycleAnalyticsForGroups: true } } });
});
it('passes requests prop to the metrics component', () => {
hasMetricsRequests(['time summary', 'recent activity']);
});
});
describe('isLoading = true', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: { isLoading: true },
});
});
it('renders the path navigation component with prop `loading` set to true', () => {
expect(findPathNavigation().props('loading')).toBe(true);
});
it('does not render the stage table', () => {
expect(findStageTable().exists()).toBe(false);
});
it('renders the overview metrics', () => {
expect(findOverviewMetrics().exists()).toBe(true);
});
it('renders the loading icon', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
});
describe('isLoadingStage = true', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: { isLoadingStage: true },
});
});
it('renders the stage table with a loading icon', () => {
const tableWrapper = findStageTable();
expect(tableWrapper.exists()).toBe(true);
expect(tableWrapper.find(GlLoadingIcon).exists()).toBe(true);
});
it('renders the path navigation loading state', () => {
expect(findPathNavigation().props('loading')).toBe(true);
});
});
describe('isEmptyStage = true', () => {
const emptyStageParams = {
isEmptyStage: true,
selectedStage: { ...selectedStage, emptyStageText: 'This stage is empty' },
};
beforeEach(() => {
wrapper = createComponent({ initialState: emptyStageParams });
});
it('renders the empty stage with `Not enough data` message', () => {
expect(findEmptyStageTitle()).toBe(NOT_ENOUGH_DATA_ERROR);
});
describe('with a selectedStageError', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
...emptyStageParams,
selectedStageError: 'There is too much data to calculate',
},
});
});
it('renders the empty stage with `There is too much data to calculate` message', () => {
expect(findEmptyStageTitle()).toBe('There is too much data to calculate');
});
});
});
describe('without enough permissions', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
selectedStage,
permissions: {
...permissions,
[selectedStage.id]: false,
},
},
});
});
it('renders the empty stage with `You need permission.` message', () => {
expect(findEmptyStageTitle()).toBe('You need permission.');
});
});
describe('without a selected stage', () => {
beforeEach(() => {
wrapper = createComponent({
initialGetters: { pathNavigationData: () => [] },
initialState: { selectedStage: null, isEmptyStage: true },
});
});
it('renders the stage table', () => {
expect(findStageTable().exists()).toBe(true);
});
it('does not render the path navigation', () => {
expect(findPathNavigation().exists()).toBe(false);
});
it('does not render the stage table events', () => {
expect(findStageEvents()).toHaveLength(0);
});
it('does not render the loading icon', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
});
});