Commit 3c38adc1 authored by Frédéric Caplette's avatar Frédéric Caplette

Merge branch '21425-add-polling-to-mini-pipeline-graph' into 'master'

Add polling to commit box mini pipeline graph

See merge request gitlab-org/gitlab!83890
parents dfd88084 04c2c5a6
...@@ -3,11 +3,20 @@ import { GlLoadingIcon } from '@gitlab/ui'; ...@@ -3,11 +3,20 @@ import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
import {
getQueryHeaders,
toggleQueryPollingByVisibility,
} from '~/pipelines/components/graph/utils';
import { formatStages } from '../utils';
import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql'; import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql';
import { PIPELINE_STAGES_POLL_INTERVAL } from '../constants';
export default { export default {
i18n: { i18n: {
linkedPipelinesFetchError: __('There was a problem fetching linked pipelines.'), linkedPipelinesFetchError: __('There was a problem fetching linked pipelines.'),
stageConversionError: __('There was a problem handling the pipeline data.'),
stagesFetchError: __('There was a problem fetching the pipeline stages.'),
}, },
components: { components: {
GlLoadingIcon, GlLoadingIcon,
...@@ -22,6 +31,9 @@ export default { ...@@ -22,6 +31,9 @@ export default {
iid: { iid: {
default: '', default: '',
}, },
graphqlResourceEtag: {
default: '',
},
}, },
props: { props: {
stages: { stages: {
...@@ -48,10 +60,31 @@ export default { ...@@ -48,10 +60,31 @@ export default {
createFlash({ message: this.$options.i18n.linkedPipelinesFetchError }); createFlash({ message: this.$options.i18n.linkedPipelinesFetchError });
}, },
}, },
pipelineStages: {
context() {
return getQueryHeaders(this.graphqlResourceEtag);
},
query: getPipelineStagesQuery,
pollInterval: PIPELINE_STAGES_POLL_INTERVAL,
variables() {
return {
fullPath: this.fullPath,
iid: this.iid,
};
},
update({ project }) {
return project?.pipeline?.stages?.nodes || [];
},
error() {
createFlash({ message: this.$options.i18n.stagesFetchError });
},
},
}, },
data() { data() {
return { return {
formattedStages: [],
pipeline: null, pipeline: null,
pipelineStages: [],
}; };
}, },
computed: { computed: {
...@@ -65,6 +98,25 @@ export default { ...@@ -65,6 +98,25 @@ export default {
return this.pipeline?.upstream; return this.pipeline?.upstream;
}, },
}, },
watch: {
pipelineStages() {
// pipelineStages are from GraphQL
// stages are from REST
// we do this to use dropdown_path for fetching jobs on stage click
try {
this.formattedStages = formatStages(this.pipelineStages, this.stages);
} catch (error) {
createFlash({
message: this.$options.i18n.stageConversionError,
captureError: true,
error,
});
}
},
},
mounted() {
toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStages);
},
}; };
</script> </script>
...@@ -79,7 +131,7 @@ export default { ...@@ -79,7 +131,7 @@ export default {
/> />
<pipeline-mini-graph <pipeline-mini-graph
:stages="stages" :stages="formattedStages"
class="gl-display-inline" class="gl-display-inline"
data-testid="commit-box-mini-graph" data-testid="commit-box-mini-graph"
/> />
......
export const PIPELINE_STAGES_POLL_INTERVAL = 10000;
query getPipelineStages($fullPath: ID!, $iid: ID!) {
project(fullPath: $fullPath) {
id
pipeline(iid: $iid) {
id
stages {
nodes {
id
name
detailedStatus {
id
icon
group
}
}
}
}
}
}
...@@ -5,7 +5,7 @@ import createDefaultClient from '~/lib/graphql'; ...@@ -5,7 +5,7 @@ import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo); Vue.use(VueApollo);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient({}, { useGet: true }),
}); });
export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipeline-mini-graph') => { export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipeline-mini-graph') => {
...@@ -15,7 +15,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin ...@@ -15,7 +15,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
return; return;
} }
const { stages, fullPath, iid } = el.dataset; const { stages, fullPath, iid, graphqlResourceEtag } = el.dataset;
// Some commits have no pipeline, code splitting to load the pipeline optionally // Some commits have no pipeline, code splitting to load the pipeline optionally
const { default: CommitBoxPipelineMiniGraph } = await import( const { default: CommitBoxPipelineMiniGraph } = await import(
...@@ -30,6 +30,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin ...@@ -30,6 +30,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
fullPath, fullPath,
iid, iid,
dataMethod: 'graphql', dataMethod: 'graphql',
graphqlResourceEtag,
}, },
render(createElement) { render(createElement) {
return createElement(CommitBoxPipelineMiniGraph, { return createElement(CommitBoxPipelineMiniGraph, {
......
export const formatStages = (graphQLStages = [], restStages = []) => {
if (graphQLStages.length !== restStages.length) {
throw new Error('Rest stages and graphQl stages must be the same length');
}
return graphQLStages.map((stage, index) => {
return {
name: stage.name,
status: stage.detailedStatus,
dropdown_path: restStages[index]?.dropdown_path || '',
title: restStages[index].title || '',
};
});
};
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
#{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) } #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
.mr-widget-pipeline-graph .mr-widget-pipeline-graph
.stage-cell .stage-cell
.js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid } } .js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
- if @last_pipeline.duration - if @last_pipeline.duration
in in
= time_interval_in_words @last_pipeline.duration = time_interval_in_words @last_pipeline.duration
......
...@@ -7,12 +7,16 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper'; ...@@ -7,12 +7,16 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash'; import createFlash from '~/flash';
import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue'; import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue';
import { PIPELINE_STAGES_POLL_INTERVAL } from '~/projects/commit_box/info/constants';
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql'; import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
import * as graphQlUtils from '~/pipelines/components/graph/utils';
import { import {
mockDownstreamQueryResponse, mockDownstreamQueryResponse,
mockUpstreamQueryResponse, mockUpstreamQueryResponse,
mockUpstreamDownstreamQueryResponse, mockUpstreamDownstreamQueryResponse,
mockStages, mockStages,
mockPipelineStagesQueryResponse,
} from '../mock_data'; } from '../mock_data';
const fullPath = 'gitlab-org/gitlab'; const fullPath = 'gitlab-org/gitlab';
...@@ -30,14 +34,22 @@ describe('Commit box pipeline mini graph', () => { ...@@ -30,14 +34,22 @@ describe('Commit box pipeline mini graph', () => {
.fn() .fn()
.mockResolvedValue(mockUpstreamDownstreamQueryResponse); .mockResolvedValue(mockUpstreamDownstreamQueryResponse);
const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph'); const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph');
const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream');
const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream');
const createMockApolloProvider = (handler) => { const advanceToNextFetch = () => {
const requestHandlers = [[getLinkedPipelinesQuery, handler]]; jest.advanceTimersByTime(PIPELINE_STAGES_POLL_INTERVAL);
};
const createMockApolloProvider = (handler = downstreamHandler) => {
const requestHandlers = [
[getLinkedPipelinesQuery, handler],
[getPipelineStagesQuery, stagesHandler],
];
return createMockApollo(requestHandlers); return createMockApollo(requestHandlers);
}; };
...@@ -52,6 +64,7 @@ describe('Commit box pipeline mini graph', () => { ...@@ -52,6 +64,7 @@ describe('Commit box pipeline mini graph', () => {
fullPath, fullPath,
iid, iid,
dataMethod: 'graphql', dataMethod: 'graphql',
graphqlResourceEtag: '/api/graphql:pipelines/id/320',
}, },
apolloProvider: createMockApolloProvider(handler), apolloProvider: createMockApolloProvider(handler),
}), }),
...@@ -64,15 +77,16 @@ describe('Commit box pipeline mini graph', () => { ...@@ -64,15 +77,16 @@ describe('Commit box pipeline mini graph', () => {
describe('loading state', () => { describe('loading state', () => {
it('should display loading state when loading', () => { it('should display loading state when loading', () => {
createComponent(downstreamHandler); createComponent();
expect(findLoadingIcon().exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(true);
expect(findMiniGraph().exists()).toBe(false);
}); });
}); });
describe('loaded state', () => { describe('loaded state', () => {
it('should not display loading state after the query is resolved', async () => { it('should not display loading state after the query is resolved', async () => {
createComponent(downstreamHandler); createComponent();
await waitForPromises(); await waitForPromises();
...@@ -81,7 +95,7 @@ describe('Commit box pipeline mini graph', () => { ...@@ -81,7 +95,7 @@ describe('Commit box pipeline mini graph', () => {
}); });
it('should pass the pipeline path prop for the counter badge', async () => { it('should pass the pipeline path prop for the counter badge', async () => {
createComponent(downstreamHandler); createComponent();
await waitForPromises(); await waitForPromises();
...@@ -105,6 +119,28 @@ describe('Commit box pipeline mini graph', () => { ...@@ -105,6 +119,28 @@ describe('Commit box pipeline mini graph', () => {
expect(findUpstream().exists()).toBe(upstreamRenders); expect(findUpstream().exists()).toBe(upstreamRenders);
}); });
}); });
it('formatted stages should be passed to the pipeline mini graph', async () => {
const stage = mockStages[0];
const expectedStages = [
{
name: stage.name,
status: {
id: stage.status.id,
icon: stage.status.icon,
group: stage.status.group,
},
dropdown_path: stage.dropdown_path,
title: stage.title,
},
];
createComponent();
await waitForPromises();
expect(findMiniGraph().props('stages')).toEqual(expectedStages);
});
}); });
describe('error state', () => { describe('error state', () => {
...@@ -118,4 +154,44 @@ describe('Commit box pipeline mini graph', () => { ...@@ -118,4 +154,44 @@ describe('Commit box pipeline mini graph', () => {
}); });
}); });
}); });
describe('polling', () => {
it('polling interval is set for pipeline stages', () => {
createComponent();
const expectedInterval = wrapper.vm.$apollo.queries.pipelineStages.options.pollInterval;
expect(expectedInterval).toBe(PIPELINE_STAGES_POLL_INTERVAL);
});
it('polls for stages', async () => {
createComponent();
await waitForPromises();
expect(stagesHandler).toHaveBeenCalledTimes(1);
advanceToNextFetch();
await waitForPromises();
expect(stagesHandler).toHaveBeenCalledTimes(2);
advanceToNextFetch();
await waitForPromises();
expect(stagesHandler).toHaveBeenCalledTimes(3);
});
it('toggles pipelineStages polling with visibility check', async () => {
jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
createComponent();
await waitForPromises();
expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
wrapper.vm.$apollo.queries.pipelineStages,
);
});
});
}); });
...@@ -104,133 +104,50 @@ export const mockUpstreamDownstreamQueryResponse = { ...@@ -104,133 +104,50 @@ export const mockUpstreamDownstreamQueryResponse = {
}, },
}; };
export const mockStages = [ export const mockPipelineStagesQueryResponse = {
data: {
project: {
id: 'gid://gitlab/Project/20',
pipeline: {
id: 'gid://gitlab/Ci::Pipeline/320',
stages: {
nodes: [
{ {
id: 'stage-1', __typename: 'CiStage',
id: 'gid://gitlab/Ci::Stage/409',
name: 'build', name: 'build',
title: 'build: passed', detailedStatus: {
status: { id: 'success-409-409',
id: 'status-1',
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/ci-project/-/pipelines/611#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/ci-project/-/pipelines/611#build',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=build',
},
{
id: 'stage-2',
name: 'test',
title: 'test: passed',
status: {
id: 'status-2',
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success', group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/ci-project/-/pipelines/611#test',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/ci-project/-/pipelines/611#test',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test',
},
{
id: 'stage-3',
name: 'test_two',
title: 'test_two: passed',
status: {
id: 'status-3',
icon: 'status_success', icon: 'status_success',
text: 'passed', __typename: 'DetailedStatus',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/ci-project/-/pipelines/611#test_two',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
}, },
path: '/root/ci-project/-/pipelines/611#test_two',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test_two',
}, },
{ ],
id: 'stage-4',
name: 'manual',
title: 'manual: skipped',
status: {
id: 'status-4',
icon: 'status_skipped',
text: 'skipped',
label: 'skipped',
group: 'skipped',
tooltip: 'skipped',
has_details: true,
details_path: '/root/ci-project/-/pipelines/611#manual',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png',
action: {
id: 'action-4',
icon: 'play',
title: 'Play all manual',
path: '/root/ci-project/-/pipelines/611/stages/manual/play_manual',
method: 'post',
button_title: 'Play all manual',
}, },
}, },
path: '/root/ci-project/-/pipelines/611#manual',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=manual',
}, },
{
id: 'stage-5',
name: 'deploy',
title: 'deploy: passed',
status: {
id: 'status-5',
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/ci-project/-/pipelines/611#deploy',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/ci-project/-/pipelines/611#deploy',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=deploy',
}, },
};
export const mockStages = [
{ {
id: 'stage-6', name: 'build',
name: 'qa', title: 'build: passed',
title: 'qa: passed',
status: { status: {
id: 'status-6', id: 'success-409-409',
icon: 'status_success', icon: 'status_success',
text: 'passed', text: 'passed',
label: 'passed', label: 'passed',
group: 'success', group: 'success',
tooltip: 'passed', tooltip: 'passed',
has_details: true, has_details: true,
details_path: '/root/ci-project/-/pipelines/611#qa', details_path: '/root/ci-project/-/pipelines/318#build',
illustration: null, illustration: null,
favicon: favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
}, },
path: '/root/ci-project/-/pipelines/611#qa', path: '/root/ci-project/-/pipelines/318#build',
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa', dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=build',
}, },
]; ];
...@@ -38011,12 +38011,18 @@ msgstr "" ...@@ -38011,12 +38011,18 @@ msgstr ""
msgid "There was a problem fetching the keep latest artifacts setting." msgid "There was a problem fetching the keep latest artifacts setting."
msgstr "" msgstr ""
msgid "There was a problem fetching the pipeline stages."
msgstr ""
msgid "There was a problem fetching the projects" msgid "There was a problem fetching the projects"
msgstr "" msgstr ""
msgid "There was a problem fetching users." msgid "There was a problem fetching users."
msgstr "" msgstr ""
msgid "There was a problem handling the pipeline data."
msgstr ""
msgid "There was a problem sending the confirmation email" msgid "There was a problem sending the confirmation email"
msgstr "" msgstr ""
......
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue'; import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue';
import { mockStages } from './mock_data'; import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
import { mockPipelineStagesQueryResponse, mockStages } from './mock_data';
jest.mock('~/flash');
Vue.use(VueApollo);
describe('Commit box pipeline mini graph', () => { describe('Commit box pipeline mini graph', () => {
let wrapper; let wrapper;
...@@ -10,34 +21,36 @@ describe('Commit box pipeline mini graph', () => { ...@@ -10,34 +21,36 @@ describe('Commit box pipeline mini graph', () => {
const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream');
const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream');
const createComponent = () => { const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
const createComponent = ({ props = {} } = {}) => {
const handlers = [
[getLinkedPipelinesQuery, {}],
[getPipelineStagesQuery, stagesHandler],
];
wrapper = extendedWrapper( wrapper = extendedWrapper(
shallowMount(CommitBoxPipelineMiniGraph, { shallowMount(CommitBoxPipelineMiniGraph, {
propsData: { propsData: {
stages: mockStages, stages: mockStages,
...props,
}, },
mocks: { apolloProvider: createMockApollo(handlers),
$apollo: {
queries: {
pipeline: {
loading: false,
},
},
},
},
}), }),
); );
};
beforeEach(() => { return waitForPromises();
createComponent(); };
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('linked pipelines', () => { describe('linked pipelines', () => {
beforeEach(async () => {
await createComponent();
});
it('should display the mini pipeine graph', () => { it('should display the mini pipeine graph', () => {
expect(findMiniGraph().exists()).toBe(true); expect(findMiniGraph().exists()).toBe(true);
}); });
...@@ -47,4 +60,18 @@ describe('Commit box pipeline mini graph', () => { ...@@ -47,4 +60,18 @@ describe('Commit box pipeline mini graph', () => {
expect(findDownstream().exists()).toBe(false); expect(findDownstream().exists()).toBe(false);
}); });
}); });
describe('when data is mismatched', () => {
beforeEach(async () => {
await createComponent({ props: { stages: [] } });
});
it('calls create flash with expected arguments', () => {
expect(createFlash).toHaveBeenCalledWith({
message: 'There was a problem handling the pipeline data.',
captureError: true,
error: new Error('Rest stages and graphQl stages must be the same length'),
});
});
});
}); });
...@@ -115,3 +115,29 @@ export const mockStages = [ ...@@ -115,3 +115,29 @@ export const mockStages = [
dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa', dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa',
}, },
]; ];
export const mockPipelineStagesQueryResponse = {
data: {
project: {
id: 'gid://gitlab/Project/20',
pipeline: {
id: 'gid://gitlab/Ci::Pipeline/320',
stages: {
nodes: [
{
__typename: 'CiStage',
id: 'gid://gitlab/Ci::Stage/409',
name: 'build',
detailedStatus: {
id: 'success-409-409',
group: 'success',
icon: 'status_success',
__typename: 'DetailedStatus',
},
},
],
},
},
},
},
};
import { formatStages } from '~/projects/commit_box/info/utils';
const graphqlStage = [
{
__typename: 'CiStage',
name: 'deploy',
detailedStatus: {
__typename: 'DetailedStatus',
icon: 'status_success',
group: 'success',
id: 'success-409-409',
},
},
];
const restStage = [
{
name: 'deploy',
title: 'deploy: passed',
status: {
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
details_path: '/root/ci-project/-/pipelines/318#deploy',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
path: '/root/ci-project/-/pipelines/318#deploy',
dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=deploy',
},
];
describe('Utils', () => {
it('combines REST and GraphQL stages correctly for component', () => {
expect(formatStages(graphqlStage, restStage)).toEqual([
{
dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=deploy',
name: 'deploy',
status: {
__typename: 'DetailedStatus',
group: 'success',
icon: 'status_success',
id: 'success-409-409',
},
title: 'deploy: passed',
},
]);
});
it('throws an error if arrays are not the same length', () => {
expect(() => {
formatStages(graphqlStage, []);
}).toThrow('Rest stages and graphQl stages must be the same length');
});
});
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