Commit 83b6738d authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '298973-needs-selector-updates' into 'master'

Pipeline Graph: Update Conditions for Showing Switcher

See merge request gitlab-org/gitlab!59174
parents 3334ec9a 1684737b
......@@ -13,5 +13,6 @@ export const GRAPHQL = 'graphql';
export const STAGE_VIEW = 'stage';
export const LAYER_VIEW = 'layer';
export const VIEW_TYPE_KEY = 'pipeline_graph_view_type';
export const IID_FAILURE = 'missing_iid';
......@@ -2,11 +2,12 @@
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { __ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import { reportToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW } from './constants';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW, VIEW_TYPE_KEY } from './constants';
import PipelineGraph from './graph_component.vue';
import GraphViewSelector from './graph_view_selector.vue';
import {
......@@ -22,6 +23,7 @@ export default {
GlAlert,
GlLoadingIcon,
GraphViewSelector,
LocalStorageSync,
PipelineGraph,
},
mixins: [glFeatureFlagMixin()],
......@@ -143,7 +145,7 @@ export default {
return this.$apollo.queries.pipeline.loading && !this.pipeline;
},
showGraphViewSelector() {
return Boolean(this.glFeatures.pipelineGraphLayersView && this.pipeline);
return Boolean(this.glFeatures.pipelineGraphLayersView && this.pipeline?.usesNeeds);
},
},
mounted() {
......@@ -184,6 +186,7 @@ export default {
this.currentViewType = type;
},
},
viewTypeKey: VIEW_TYPE_KEY,
};
</script>
<template>
......@@ -191,11 +194,17 @@ export default {
<gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
{{ alert.text }}
</gl-alert>
<local-storage-sync
:storage-key="$options.viewTypeKey"
:value="currentViewType"
@input="updateViewType"
>
<graph-view-selector
v-if="showGraphViewSelector"
:type="currentViewType"
@updateViewType="updateViewType"
/>
</local-storage-sync>
<gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph
v-if="pipeline"
......
......@@ -57,7 +57,7 @@ export default {
<template>
<div class="gl-display-flex gl-align-items-center gl-my-4">
<span>{{ $options.i18n.labelText }}</span>
<gl-dropdown class="gl-ml-4">
<gl-dropdown data-testid="pipeline-view-selector" class="gl-ml-4">
<template #button-content>
<gl-sprintf :message="currentDropdownText">
<template #code="{ content }">
......
......@@ -27,6 +27,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
__typename
id
iid
usesNeeds
downstream {
__typename
nodes {
......
......@@ -2,9 +2,15 @@ import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
import {
IID_FAILURE,
LAYER_VIEW,
STAGE_VIEW,
VIEW_TYPE_KEY,
} from '~/pipelines/components/graph/constants';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
......@@ -21,6 +27,7 @@ const defaultProvide = {
describe('Pipeline graph wrapper', () => {
Vue.use(VueApollo);
useLocalStorageSpy();
let wrapper;
const getAlert = () => wrapper.find(GlAlert);
......@@ -216,7 +223,7 @@ describe('Pipeline graph wrapper', () => {
});
describe('view dropdown', () => {
describe('when feature flag is off', () => {
describe('when pipelineGraphLayersView feature flag is off', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.runOnlyPendingTimers();
......@@ -228,7 +235,7 @@ describe('Pipeline graph wrapper', () => {
});
});
describe('when feature flag is on', () => {
describe('when pipelineGraphLayersView feature flag is on', () => {
let layersFn;
beforeEach(async () => {
layersFn = jest.spyOn(parsingUtils, 'listByLayers');
......@@ -245,7 +252,7 @@ describe('Pipeline graph wrapper', () => {
await wrapper.vm.$nextTick();
});
it('appears', () => {
it('appears when pipeline uses needs', () => {
expect(getViewSelector().exists()).toBe(true);
});
......@@ -259,6 +266,11 @@ describe('Pipeline graph wrapper', () => {
expect(getStageColumnTitle().text()).toBe('');
});
it('saves the view type to local storage', async () => {
await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
expect(localStorage.setItem.mock.calls).toEqual([[VIEW_TYPE_KEY, LAYER_VIEW]]);
});
it('calls listByLayers only once no matter how many times view is switched', async () => {
expect(layersFn).not.toHaveBeenCalled();
await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
......@@ -269,5 +281,53 @@ describe('Pipeline graph wrapper', () => {
expect(layersFn).toHaveBeenCalledTimes(1);
});
});
describe('when feature flag is on and local storage is set', () => {
beforeEach(async () => {
localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW);
createComponentWithApollo({
provide: {
glFeatures: {
pipelineGraphLayersView: true,
},
},
mountFn: mount,
});
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('reads the view type from localStorage when available', () => {
expect(wrapper.find('[data-testid="pipeline-view-selector"] code').text()).toContain(
'needs:',
);
});
});
describe('when feature flag is on but pipeline does not use needs', () => {
beforeEach(async () => {
const nonNeedsResponse = { ...mockPipelineResponse };
nonNeedsResponse.data.project.pipeline.usesNeeds = false;
createComponentWithApollo({
provide: {
glFeatures: {
pipelineGraphLayersView: true,
},
},
mountFn: mount,
getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
});
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('does not appear when pipeline does not use needs', () => {
expect(getViewSelector().exists()).toBe(false);
});
});
});
});
......@@ -8,6 +8,7 @@ export const mockPipelineResponse = {
__typename: 'Pipeline',
id: 163,
iid: '22',
usesNeeds: true,
downstream: null,
upstream: null,
stages: {
......@@ -569,6 +570,7 @@ export const wrappedPipelineReturn = {
__typename: 'Pipeline',
id: 'gid://gitlab/Ci::Pipeline/175',
iid: '38',
usesNeeds: true,
downstream: {
__typename: 'PipelineConnection',
nodes: [],
......
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