diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
commit | 0c872e02b2c822e3397515ec324051ff540f0cd5 (patch) | |
tree | ce2fb6ce7030e4dad0f4118d21ab6453e5938cdd /spec/frontend/analytics/cycle_analytics/base_spec.js | |
parent | f7e05a6853b12f02911494c4b3fe53d9540d74fc (diff) |
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'spec/frontend/analytics/cycle_analytics/base_spec.js')
-rw-r--r-- | spec/frontend/analytics/cycle_analytics/base_spec.js | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/spec/frontend/analytics/cycle_analytics/base_spec.js b/spec/frontend/analytics/cycle_analytics/base_spec.js new file mode 100644 index 00000000000..58588ff49ce --- /dev/null +++ b/spec/frontend/analytics/cycle_analytics/base_spec.js @@ -0,0 +1,265 @@ +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 ValueStreamMetrics from '~/analytics/shared/components/value_stream_metrics.vue'; +import BaseComponent from '~/analytics/cycle_analytics/components/base.vue'; +import PathNavigation from '~/analytics/cycle_analytics/components/path_navigation.vue'; +import StageTable from '~/analytics/cycle_analytics/components/stage_table.vue'; +import ValueStreamFilters from '~/analytics/cycle_analytics/components/value_stream_filters.vue'; +import { NOT_ENOUGH_DATA_ERROR } from '~/analytics/cycle_analytics/constants'; +import initState from '~/analytics/cycle_analytics/store/state'; +import { + transformedProjectStagePathData, + selectedStage, + issueEvents, + createdBefore, + createdAfter, + currentGroup, + stageCounts, + initialPaginationState as pagination, +} 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 = { + 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 findPagination = () => wrapper.findByTestId('vsa-stage-pagination'); + +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, pagination } }); + }); + + 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); + }); + + it('renders pagination', () => { + expect(findPagination().exists()).toBe(true); + }); + + 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.findComponent(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 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); + }); + }); +}); |