Commit 937099d4 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Added path navigation to project VSA

Adds the path navigation to project level
VSA and removes the existing horizontal
navigation. Update path navigation selectors.

Changelog: changed
EE: true
parent 660b6a1e
...@@ -117,7 +117,6 @@ export default { ...@@ -117,7 +117,6 @@ export default {
<div class="cycle-analytics"> <div class="cycle-analytics">
<path-navigation <path-navigation
v-if="shouldDisplayPathNavigation" v-if="shouldDisplayPathNavigation"
:key="`path_navigation_key_${pathNavigationData.length}`"
class="js-path-navigation gl-w-full gl-pb-2" class="js-path-navigation gl-w-full gl-pb-2"
:loading="isLoading" :loading="isLoading"
:stages="pathNavigationData" :stages="pathNavigationData"
......
...@@ -177,7 +177,6 @@ export default { ...@@ -177,7 +177,6 @@ export default {
<div v-if="!shouldRenderEmptyState" class="gl-max-w-full"> <div v-if="!shouldRenderEmptyState" class="gl-max-w-full">
<path-navigation <path-navigation
v-if="selectedStageReady" v-if="selectedStageReady"
:key="`path_navigation_key_${pathNavigationData.length}`"
class="js-path-navigation gl-w-full gl-pb-2" class="js-path-navigation gl-w-full gl-pb-2"
:loading="isLoading" :loading="isLoading"
:stages="pathNavigationData" :stages="pathNavigationData"
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Component from '~/cycle_analytics/components/path_navigation.vue'; import Component from '~/cycle_analytics/components/path_navigation.vue';
import { OVERVIEW_STAGE_ID } from '~/cycle_analytics/constants';
import { transformedStagePathData, issueStage } from '../mock_data'; import { transformedStagePathData, issueStage } from '../mock_data';
describe('Group PathNavigation', () => { describe('Group PathNavigation', () => {
let wrapper = null; let wrapper = null;
const createComponent = (props) => { const createComponent = (props) => {
return mount(Component, { return extendedWrapper(
propsData: { mount(Component, {
stages: transformedStagePathData, propsData: {
selectedStage: issueStage, stages: transformedStagePathData,
loading: false, selectedStage: issueStage,
...props, loading: false,
}, ...props,
}); },
}),
);
}; };
const pathNavigationItems = () => { const pathNavigationItems = () => {
return wrapper.findAll('.gl-path-nav-list-item'); return wrapper.findByTestId('gl-path-nav').findAll('li');
}; };
const pathItemContent = () => pathNavigationItems().wrappers;
const stagesWithCounts = transformedStagePathData.filter(
(stage) => stage.id !== OVERVIEW_STAGE_ID,
);
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
}); });
...@@ -35,8 +45,7 @@ describe('Group PathNavigation', () => { ...@@ -35,8 +45,7 @@ describe('Group PathNavigation', () => {
}); });
it('renders popovers for all stages except for the overview stage', () => { it('renders popovers for all stages except for the overview stage', () => {
const pathItemContent = pathNavigationItems().wrappers; const [overviewStage, ...popoverStages] = pathItemContent();
const [overviewStage, ...popoverStages] = pathItemContent;
expect(overviewStage.text()).toContain('Overview'); expect(overviewStage.text()).toContain('Overview');
expect(overviewStage.find('[data-testid="stage-item-popover"]').exists()).toBe(false); expect(overviewStage.find('[data-testid="stage-item-popover"]').exists()).toBe(false);
...@@ -63,5 +72,14 @@ describe('Group PathNavigation', () => { ...@@ -63,5 +72,14 @@ describe('Group PathNavigation', () => {
const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0); const firstPopover = wrapper.findAll('[data-testid="stage-item-popover"]').at(0);
expect(firstPopover.text()).toContain('Stage time (median)'); expect(firstPopover.text()).toContain('Stage time (median)');
}); });
it('renders each stage with its stage count', () => {
const popoverStages = pathItemContent().slice(1); // skip the first stage, the overview does not have a popover
popoverStages.forEach((stage, index) => {
const content = stage.find('[data-testid="stage-item-popover"]').html();
expect(content).toContain('Items in stage');
expect(content).toContain(`${stagesWithCounts[index].stageCount} items`);
});
});
}); });
}); });
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project PathNavigation displays correctly loading is false matches the snapshot 1`] = `
<div
class="gl-path-nav"
data-testid="gl-path-nav"
style="--path-bg-color: white;"
>
<span
class="gl-path-fade gl-path-fade-left"
style="display: none;"
>
<button
aria-label="Scroll left"
class="gl-clear-icon-button"
>
<svg
aria-hidden="true"
class="gl-icon s32"
data-testid="chevron-left-icon"
role="img"
>
<use
href="#chevron-left"
/>
</svg>
</button>
</span>
<ul
class="gl-path-nav-list"
>
<li
class="gl-path-nav-list-item"
id="path-6-item-0"
>
<button
class="gl-path-button gl-path-active-item-indigo"
>
<!---->
Issue
<span
class="gl-font-weight-normal gl-pl-2"
>
172800
</span>
</button>
<div
class="gl-popover"
data-testid="stage-item-popover"
>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Stage time (median)
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
172800
</div>
</div>
</div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
-
</div>
</div>
</div>
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<!---->
<!---->
</div>
Issue
</div>
</li>
<li
class="gl-path-nav-list-item"
id="path-6-item-1"
>
<button
class="gl-path-button"
>
<!---->
Plan
<span
class="gl-font-weight-normal gl-pl-2"
>
86400
</span>
</button>
<div
class="gl-popover"
data-testid="stage-item-popover"
>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Stage time (median)
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
86400
</div>
</div>
</div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
-
</div>
</div>
</div>
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<!---->
<!---->
</div>
Plan
</div>
</li>
<li
class="gl-path-nav-list-item"
id="path-6-item-2"
>
<button
class="gl-path-button"
>
<!---->
Code
<span
class="gl-font-weight-normal gl-pl-2"
>
129600
</span>
</button>
<div
class="gl-popover"
data-testid="stage-item-popover"
>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Stage time (median)
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
129600
</div>
</div>
</div>
<div
class="gl-px-4"
>
<div
class="gl-display-flex gl-justify-content-space-between"
>
<div
class="gl-pr-4 gl-pb-4"
>
Items in stage
</div>
<div
class="gl-pb-4 gl-font-weight-bold"
>
-
</div>
</div>
</div>
<div
class="gl-px-4 gl-pt-4 gl-border-t-1 gl-border-t-solid gl-border-gray-50"
>
<!---->
<!---->
</div>
Code
</div>
</li>
</ul>
<span
class="gl-path-fade gl-path-fade-right"
style="display: none;"
>
<button
aria-label="Scroll right"
class="gl-clear-icon-button"
>
<svg
aria-hidden="true"
class="gl-icon s32"
data-testid="chevron-right-icon"
role="img"
>
<use
href="#chevron-right"
/>
</svg>
</button>
</span>
</div>
`;
import { GlPath, GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import { GlPath, GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Component from '~/cycle_analytics/components/path_navigation.vue'; import Component from '~/cycle_analytics/components/path_navigation.vue';
import { transformedProjectStagePathData, selectedStage } from './mock_data'; import { transformedProjectStagePathData, selectedStage } from './mock_data';
...@@ -7,26 +8,34 @@ describe('Project PathNavigation', () => { ...@@ -7,26 +8,34 @@ describe('Project PathNavigation', () => {
let wrapper = null; let wrapper = null;
const createComponent = (props) => { const createComponent = (props) => {
return mount(Component, { return extendedWrapper(
propsData: { mount(Component, {
stages: transformedProjectStagePathData, propsData: {
selectedStage, stages: transformedProjectStagePathData,
loading: false, selectedStage,
...props, loading: false,
}, ...props,
}); },
}),
);
};
const findPathNavigation = () => {
return wrapper.findByTestId('gl-path-nav');
}; };
const pathNavigationTitles = () => { const findPathNavigationItems = () => {
return wrapper.findAll('.gl-path-button'); return findPathNavigation().findAll('li');
}; };
const pathNavigationItems = () => { const findPathNavigationTitles = () => {
return wrapper.findAll('.gl-path-nav-list-item'); return findPathNavigation()
.findAll('li button')
.wrappers.map((w) => w.html());
}; };
const clickItemAt = (index) => { const clickItemAt = (index) => {
pathNavigationTitles().at(index).trigger('click'); findPathNavigationItems().at(index).find('button').trigger('click');
}; };
beforeEach(() => { beforeEach(() => {
...@@ -61,9 +70,17 @@ describe('Project PathNavigation', () => { ...@@ -61,9 +70,17 @@ describe('Project PathNavigation', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false); expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);
}); });
// TODO: make this test more granular it('renders each stage', () => {
it('matches the snapshot', () => { const result = findPathNavigationTitles();
expect(wrapper.element).toMatchSnapshot(); expect(result.length).toBe(transformedProjectStagePathData.length);
});
it('renders each stage with its median', () => {
const result = findPathNavigationTitles();
transformedProjectStagePathData.forEach(({ title, metric }, index) => {
expect(result[index]).toContain(title);
expect(result[index]).toContain(metric);
});
}); });
describe('popovers', () => { describe('popovers', () => {
...@@ -72,7 +89,7 @@ describe('Project PathNavigation', () => { ...@@ -72,7 +89,7 @@ describe('Project PathNavigation', () => {
}); });
it('renders popovers for all stages', () => { it('renders popovers for all stages', () => {
const pathItemContent = pathNavigationItems().wrappers; const pathItemContent = findPathNavigationItems().wrappers;
pathItemContent.forEach((stage) => { pathItemContent.forEach((stage) => {
expect(stage.find('[data-testid="stage-item-popover"]').exists()).toBe(true); expect(stage.find('[data-testid="stage-item-popover"]').exists()).toBe(true);
......
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