Commit b4705bb2 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Add project vsa base specs

Adds base specs for the project level vsa
to test the rendered view

Migrate path navigation component to CE

Moves the VSA path navigation component from
EE to CE and updates tests

Updates the gitlab.pot file
parent 0fc6db6a
......@@ -2,7 +2,7 @@
import { GlIcon, GlEmptyState, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { mapActions, mapState, mapGetters } from 'vuex';
import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigation.vue';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import { __ } from '~/locale';
import banner from './banner.vue';
import stageCodeComponent from './stage_code_component.vue';
......@@ -132,7 +132,7 @@ export default {
For now we can use the `withStageCounts` flag to ensure we don't display empty stage counts
Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/326705
-->
<div class="card">
<div class="card" data-testid="vsa-stage-overview-metrics">
<div class="card-header">{{ __('Recent Project Activity') }}</div>
<div class="d-flex justify-content-between">
<div v-for="item in summary" :key="item.title" class="gl-flex-grow-1 gl-text-center">
......@@ -163,7 +163,7 @@ export default {
</div>
</div>
</div>
<div class="stage-panel-container">
<div class="stage-panel-container" data-testid="vsa-stage-table">
<div class="card stage-panel gl-px-5">
<div class="card-header border-bottom-0">
<nav class="col-headers">
......
export const DEFAULT_DAYS_TO_DISPLAY = 30;
export const OVERVIEW_STAGE_ID = 'overview';
import { roundToNearestHalf, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { unescape } from 'lodash';
import { sanitize } from '~/lib/dompurify';
import { roundToNearestHalf, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { parseSeconds } from '~/lib/utils/datetime_utility';
import { dasherize } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '../locale';
......
<script>
import { GlEmptyState } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import UrlSync from '~/vue_shared/components/url_sync.vue';
import DateRange from '../../shared/components/daterange.vue';
import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue';
import { DATE_RANGE_LIMIT } from '../../shared/constants';
import { toYmd } from '../../shared/utils';
import { PROJECTS_PER_PAGE, OVERVIEW_STAGE_ID } from '../constants';
import { PROJECTS_PER_PAGE } from '../constants';
import DurationChart from './duration_chart.vue';
import FilterBar from './filter_bar.vue';
import Metrics from './metrics.vue';
import PathNavigation from './path_navigation.vue';
import StageTableNew from './stage_table_new.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue';
import ValueStreamSelect from './value_stream_select.vue';
......
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import { __, s__ } from '~/locale';
export const PROJECTS_PER_PAGE = 50;
......@@ -52,7 +53,6 @@ export const OVERVIEW_METRICS = {
export const FETCH_VALUE_STREAM_DATA = 'fetchValueStreamData';
export const OVERVIEW_STAGE_ID = 'overview';
export const OVERVIEW_STAGE_CONFIG = {
id: OVERVIEW_STAGE_ID,
slug: OVERVIEW_STAGE_ID,
......
import dateFormat from 'dateformat';
import { isNumber } from 'lodash';
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import {
filterStagesByHiddenStatus,
pathNavigationData as basePathNavigationData,
......@@ -8,12 +9,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import httpStatus from '~/lib/utils/http_status';
import { filterToQueryObject } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { dateFormats } from '../../shared/constants';
import {
DEFAULT_VALUE_STREAM_ID,
OVERVIEW_STAGE_CONFIG,
PAGINATION_TYPE,
OVERVIEW_STAGE_ID,
} from '../constants';
import { DEFAULT_VALUE_STREAM_ID, OVERVIEW_STAGE_CONFIG, PAGINATION_TYPE } from '../constants';
export const hasNoAccessError = (state) => state.errorCode === httpStatus.FORBIDDEN;
......
import dateFormat from 'dateformat';
import { isNumber } from 'lodash';
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import { medianTimeToParsedSeconds } from '~/cycle_analytics/utils';
import createFlash, { hideFlash } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......@@ -8,7 +9,6 @@ import httpStatus from '~/lib/utils/http_status';
import { convertToSnakeCase, slugify } from '~/lib/utils/text_utility';
import { dateFormats } from '../shared/constants';
import { toYmd } from '../shared/utils';
import { OVERVIEW_STAGE_ID } from './constants';
const EVENT_TYPE_LABEL = 'label';
......
......@@ -7,7 +7,6 @@ import Component from 'ee/analytics/cycle_analytics/components/base.vue';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import FilterBar from 'ee/analytics/cycle_analytics/components/filter_bar.vue';
import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue';
import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigation.vue';
import StageTableNew from 'ee/analytics/cycle_analytics/components/stage_table_new.vue';
import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue';
......@@ -20,6 +19,7 @@ import Daterange from 'ee/analytics/shared/components/daterange.vue';
import ProjectsDropdownFilter from 'ee/analytics/shared/components/projects_dropdown_filter.vue';
import { toYmd } from 'ee/analytics/shared/utils';
import waitForPromises from 'helpers/wait_for_promises';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import * as commonUtils from '~/lib/utils/common_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......@@ -101,7 +101,7 @@ async function shouldMergeUrlParams(wrapper, result) {
expect(commonUtils.historyPushState).toHaveBeenCalled();
}
describe('Value Stream Analytics component', () => {
describe('EE Value Stream Analytics component', () => {
let wrapper;
let mock;
let store;
......
import { GlPath, GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Component from 'ee/analytics/cycle_analytics/components/path_navigation.vue';
import Component from '~/cycle_analytics/components/path_navigation.vue';
import { transformedStagePathData, issueStage } from '../mock_data';
describe('PathNavigation', () => {
describe('Group PathNavigation', () => {
let wrapper = null;
const createComponent = (props) => {
......@@ -17,18 +16,10 @@ describe('PathNavigation', () => {
});
};
const pathNavigationTitles = () => {
return wrapper.findAll('.gl-path-button');
};
const pathNavigationItems = () => {
return wrapper.findAll('.gl-path-nav-list-item');
};
const clickItemAt = (index) => {
pathNavigationTitles().at(index).trigger('click');
};
beforeEach(() => {
wrapper = createComponent();
});
......@@ -38,100 +29,39 @@ describe('PathNavigation', () => {
wrapper = null;
});
describe('displays correctly', () => {
it('has the correct props', () => {
expect(wrapper.find(GlPath).props('items')).toMatchObject(transformedStagePathData);
describe('popovers', () => {
beforeEach(() => {
wrapper = createComponent({ stages: transformedStagePathData });
});
it('contains all the expected stages', () => {
const html = wrapper.find(GlPath).html();
transformedStagePathData.forEach((stage) => {
expect(html).toContain(stage.title);
});
});
describe('loading', () => {
describe('is false', () => {
it('displays the gl-path component', () => {
expect(wrapper.find(GlPath).exists()).toBe(true);
});
it('hides the gl-skeleton-loading component', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);
});
// TODO: make this test more granular
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('popovers', () => {
beforeEach(() => {
wrapper = createComponent({ stages: transformedStagePathData });
});
it('renders popovers for all stages except for the overview stage', () => {
const pathItemContent = pathNavigationItems().wrappers;
const [overviewStage, ...popoverStages] = pathItemContent;
it('renders popovers for all stages except for the overview stage', () => {
const pathItemContent = pathNavigationItems().wrappers;
const [overviewStage, ...popoverStages] = pathItemContent;
expect(overviewStage.text()).toContain('Overview');
expect(overviewStage.find('[data-testid="stage-item-popover"]').exists()).toBe(false);
expect(overviewStage.text()).toContain('Overview');
expect(overviewStage.find('[data-testid="stage-item-popover"]').exists()).toBe(false);
popoverStages.forEach((stage) => {
expect(stage.find('[data-testid="stage-item-popover"]').exists()).toBe(true);
});
});
it('shows the sanitized start event description for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
const expectedStartEventDescription = 'Issue created';
expect(firstPopover.text()).toContain(expectedStartEventDescription);
});
it('shows the sanitized end event description for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
const expectedStartEventDescription =
'Issue first associated with a milestone or issue first added to a board';
expect(firstPopover.text()).toContain(expectedStartEventDescription);
});
it('shows the median stage time for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
expect(firstPopover.text()).toContain('Stage time (median)');
});
});
});
describe('is true', () => {
beforeEach(() => {
wrapper = createComponent({ loading: true });
});
it('hides the gl-path component', () => {
expect(wrapper.find(GlPath).exists()).toBe(false);
});
it('displays the gl-skeleton-loading component', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(true);
});
popoverStages.forEach((stage) => {
expect(stage.find('[data-testid="stage-item-popover"]').exists()).toBe(true);
});
});
});
describe('event handling', () => {
it('emits the selected event', () => {
expect(wrapper.emitted('selected')).toBeUndefined();
it('shows the sanitized start event description for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
const expectedStartEventDescription = 'Issue created';
expect(firstPopover.text()).toContain(expectedStartEventDescription);
});
clickItemAt(0);
clickItemAt(1);
clickItemAt(2);
it('shows the sanitized end event description for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
const expectedStartEventDescription =
'Issue first associated with a milestone or issue first added to a board';
expect(firstPopover.text()).toContain(expectedStartEventDescription);
});
expect(wrapper.emitted().selected).toEqual([
[transformedStagePathData[0]],
[transformedStagePathData[1]],
[transformedStagePathData[2]],
]);
it('shows the median stage time for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
expect(firstPopover.text()).toContain('Stage time (median)');
});
});
});
......@@ -529,10 +529,7 @@ describe('Value Stream Analytics actions', () => {
...state,
stages,
currentGroup,
featureFlags: {
...state.featureFlags,
hasPathNavigation: true,
},
featureFlags: state.featureFlags,
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.stageCount).reply(httpStatusCodes.OK, { events: [] });
......
......@@ -13,12 +13,12 @@ import {
endDate,
allowedStages,
selectedProjects,
transformedStagePathData,
issueStage,
stageMedians,
stageCounts,
basePaginationResult,
initialPaginationState,
transformedStagePathData,
} from '../mock_data';
let state = null;
......
import { isNumber } from 'lodash';
import { OVERVIEW_STAGE_ID } from 'ee/analytics/cycle_analytics/constants';
import {
isStartEvent,
isLabelEvent,
......@@ -21,6 +20,7 @@ import {
} from 'ee/analytics/cycle_analytics/utils';
import { toYmd } from 'ee/analytics/shared/utils';
import { rawStageMedians } from 'jest/cycle_analytics/mock_data';
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import { medianTimeToParsedSeconds } from '~/cycle_analytics/utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { slugify } from '~/lib/utils/text_utility';
......
......@@ -20460,9 +20460,6 @@ msgstr ""
msgid "Measured in bytes of code. Excludes generated and vendored code."
msgstr ""
msgid "Median"
msgstr ""
msgid "Medium Timeout Period"
msgstr ""
......@@ -25668,9 +25665,6 @@ msgstr ""
msgid "ProjectLastActivity|Never"
msgstr ""
msgid "ProjectLifecycle|Stage"
msgstr ""
msgid "ProjectOverview|Fork"
msgstr ""
......@@ -32563,9 +32557,6 @@ msgstr ""
msgid "The password for your GitLab account on %{link_to_gitlab} has successfully been changed."
msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
msgid "The pipeline has been deleted"
msgstr ""
......@@ -32710,9 +32701,6 @@ msgstr ""
msgid "The username for the Jenkins server."
msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
msgid "The value of the provided variable exceeds the %{count} character limit"
msgstr ""
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PathNavigation displays correctly loading is false matches the snapshot 1`] = `
exports[`Project PathNavigation displays correctly loading is false matches the snapshot 1`] = `
<div
class="gl-path-nav"
data-testid="gl-path-nav"
......@@ -33,29 +33,6 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<li
class="gl-path-nav-list-item"
id="path-6-item-0"
>
<button
class="gl-path-button"
>
<svg
aria-hidden="true"
class="gl-mr-2 gl-icon s16"
data-testid="gl-path-item-icon"
role="img"
>
<use
href="#home"
/>
</svg>
Overview
<!---->
</button>
</li>
<li
class="gl-path-nav-list-item"
id="path-6-item-1"
>
<button
class="gl-path-button gl-path-active-item-indigo"
......@@ -63,7 +40,11 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<!---->
Issue
<!---->
<span
class="gl-font-weight-normal gl-pl-2"
>
172800
</span>
</button>
<div
......@@ -88,7 +69,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
172800
</div>
</div>
</div>
......@@ -110,7 +91,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
172800 items
-
</div>
</div>
</div>
......@@ -118,58 +99,16 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 gl-pb-4 metric-label"
>
Start
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-pb-4 stage-event-description"
>
<p
data-sourcepos="1:1-1:13"
dir="auto"
>
Issue created
</p>
</div>
</div>
<!---->
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 metric-label"
>
Stop
</div>
<div
class="gl-display-flex gl-flex-direction-column stage-event-description"
>
<p
data-sourcepos="1:1-1:71"
dir="auto"
>
Issue first associated with a milestone or issue first added to a board
</p>
</div>
</div>
<!---->
</div>
Issue
</div>
</li>
<li
class="gl-path-nav-list-item"
id="path-6-item-2"
id="path-6-item-1"
>
<button
class="gl-path-button"
......@@ -177,7 +116,11 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<!---->
Plan
<!---->
<span
class="gl-font-weight-normal gl-pl-2"
>
86400
</span>
</button>
<div
......@@ -202,7 +145,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
86400
</div>
</div>
</div>
......@@ -224,7 +167,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
86400 items
-
</div>
</div>
</div>
......@@ -232,58 +175,16 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 gl-pb-4 metric-label"
>
Start
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-pb-4 stage-event-description"
>
<p
data-sourcepos="1:1-1:71"
dir="auto"
>
Issue first associated with a milestone or issue first added to a board
</p>
</div>
</div>
<!---->
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 metric-label"
>
Stop
</div>
<div
class="gl-display-flex gl-flex-direction-column stage-event-description"
>
<p
data-sourcepos="1:1-1:33"
dir="auto"
>
Issue first mentioned in a commit
</p>
</div>
</div>
<!---->
</div>
Plan
</div>
</li>
<li
class="gl-path-nav-list-item"
id="path-6-item-3"
id="path-6-item-2"
>
<button
class="gl-path-button"
......@@ -291,7 +192,11 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<!---->
Code
<!---->
<span
class="gl-font-weight-normal gl-pl-2"
>
129600
</span>
</button>
<div
......@@ -316,7 +221,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
129600
</div>
</div>
</div>
......@@ -338,7 +243,7 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-pb-4 gl-font-weight-bold"
>
129600 items
-
</div>
</div>
</div>
......@@ -346,51 +251,9 @@ exports[`PathNavigation displays correctly loading is false matches the snapshot
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 gl-pb-4 metric-label"
>
Start
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-pb-4 stage-event-description"
>
<p
data-sourcepos="1:1-1:33"
dir="auto"
>
Issue first mentioned in a commit
</p>
</div>
</div>
<!---->
<div
class="gl-display-flex gl-flex-direction-row"
>
<div
class="gl-display-flex gl-flex-direction-column gl-pr-4 metric-label"
>
Stop
</div>
<div
class="gl-display-flex gl-flex-direction-column stage-event-description"
>
<p
data-sourcepos="1:1-1:21"
dir="auto"
>
Merge request created
</p>
</div>
</div>
<!---->
</div>
Code
</div>
......
describe.skip(() => {});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Component from '~/cycle_analytics/components/base.vue';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import createStore from '~/cycle_analytics/store';
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const localVue = createLocalVue();
localVue.use(Vuex);
let wrapper;
function createComponent() {
const store = createStore();
return extendedWrapper(
shallowMount(Component, {
localVue,
store,
propsData: {
noDataSvgPath,
noAccessSvgPath,
},
}),
);
}
const findPathNavigation = () => wrapper.findComponent(PathNavigation);
const findOverviewMetrics = () => wrapper.findByTestId('vsa-stage-overview-metrics');
const findStageTable = () => wrapper.findByTestId('vsa-stage-table');
describe('Value stream analytics component', () => {
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders the path navigation component', () => {
expect(findPathNavigation().exists()).toBe(true);
});
it('renders the overview metrics', () => {
expect(findOverviewMetrics().exists()).toBe(true);
});
it('renders the stage table', () => {
expect(findStageTable().exists()).toBe(true);
});
});
......@@ -43,7 +43,7 @@ const planStage = {
name: 'plan',
legend: '',
description: 'Time before an issue starts implementation',
value: 'about 21 hours',
value: 75600,
};
const codeStage = {
......@@ -52,7 +52,7 @@ const codeStage = {
name: 'code',
legend: '',
description: 'Time until first merge request',
value: '2 days',
value: 172800,
};
const testStage = {
......@@ -61,7 +61,7 @@ const testStage = {
name: 'test',
legend: '',
description: 'Total test time for all commits/merges',
value: 'about 5 hours',
value: 17550,
};
const reviewStage = {
......@@ -79,7 +79,7 @@ const stagingStage = {
name: 'staging',
legend: '',
description: 'From merge request merge until deploy to production',
value: '2 days',
value: 172800,
};
export const selectedStage = {
......@@ -91,7 +91,6 @@ export const selectedStage = {
'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
component: 'stage-issue-component',
slug: 'issue',
id: 'issue',
};
export const stats = [issueStage, planStage, codeStage, testStage, reviewStage, stagingStage];
......
import { GlPath, GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Component from '~/cycle_analytics/components/path_navigation.vue';
import { transformedProjectStagePathData, selectedStage } from './mock_data';
describe('Project PathNavigation', () => {
let wrapper = null;
const createComponent = (props) => {
return mount(Component, {
propsData: {
stages: transformedProjectStagePathData,
selectedStage,
loading: false,
...props,
},
});
};
const pathNavigationTitles = () => {
return wrapper.findAll('.gl-path-button');
};
const pathNavigationItems = () => {
return wrapper.findAll('.gl-path-nav-list-item');
};
const clickItemAt = (index) => {
pathNavigationTitles().at(index).trigger('click');
};
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('displays correctly', () => {
it('has the correct props', () => {
expect(wrapper.find(GlPath).props('items')).toMatchObject(transformedProjectStagePathData);
});
it('contains all the expected stages', () => {
const html = wrapper.find(GlPath).html();
transformedProjectStagePathData.forEach((stage) => {
expect(html).toContain(stage.title);
});
});
describe('loading', () => {
describe('is false', () => {
it('displays the gl-path component', () => {
expect(wrapper.find(GlPath).exists()).toBe(true);
});
it('hides the gl-skeleton-loading component', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);
});
// TODO: make this test more granular
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('popovers', () => {
beforeEach(() => {
wrapper = createComponent({ stages: transformedProjectStagePathData });
});
it('renders popovers for all stages', () => {
const pathItemContent = pathNavigationItems().wrappers;
pathItemContent.forEach((stage) => {
expect(stage.find('[data-testid="stage-item-popover"]').exists()).toBe(true);
});
});
it('shows the median stage time for the first stage item', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
expect(firstPopover.text()).toContain('Stage time (median)');
});
});
});
describe('is true', () => {
beforeEach(() => {
wrapper = createComponent({ loading: true });
});
it('hides the gl-path component', () => {
expect(wrapper.find(GlPath).exists()).toBe(false);
});
it('displays the gl-skeleton-loading component', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(true);
});
});
});
});
describe('event handling', () => {
it('emits the selected event', () => {
expect(wrapper.emitted('selected')).toBeUndefined();
clickItemAt(0);
clickItemAt(1);
clickItemAt(2);
expect(wrapper.emitted().selected).toEqual([
[transformedProjectStagePathData[0]],
[transformedProjectStagePathData[1]],
[transformedProjectStagePathData[2]],
]);
});
});
});
......@@ -6,7 +6,6 @@ import {
selectedStage,
} from '../mock_data';
// TODO: move path navigation component to CE ee/spec/frontend/analytics/cycle_analytics/components/path_navigation_spec.js
describe('Value stream analytics getters', () => {
describe('pathNavigationData', () => {
it('returns the transformed data', () => {
......
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