diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue index 3662100d5266c5ce8c3c932105ae02ace15788ca..7beabcfe4035dfc63351f9795c37fd15b1436f77 100644 --- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue +++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue @@ -10,6 +10,8 @@ export default { }, components: { PipelineMiniGraph, + LinkedPipelinesMiniList: () => + import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, inject: ['projectFullPath'], props: { @@ -45,6 +47,9 @@ export default { downstreamPipelines() { return this.linkedPipelines?.downstream?.nodes || []; }, + hasDownstreamPipelines() { + return this.downstreamPipelines.length > 0; + }, hasPipelineStages() { return this.pipelineStages.length > 0; }, @@ -82,11 +87,23 @@ export default { </script> <template> - <pipeline-mini-graph + <div v-if="hasPipelineStages" - :downstream-pipelines="downstreamPipelines" - :pipeline-path="pipelinePath" - :stages="pipelineStages" - :upstream-pipeline="upstreamPipeline" - /> + class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5" + > + <linked-pipelines-mini-list + v-if="upstreamPipeline" + :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ + upstreamPipeline, + ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" + data-testid="pipeline-editor-mini-graph-upstream" + /> + <pipeline-mini-graph :stages="pipelineStages" /> + <linked-pipelines-mini-list + v-if="hasDownstreamPipelines" + :triggered="downstreamPipelines" + :pipeline-path="pipelinePath" + data-testid="pipeline-editor-mini-graph-downstream" + /> + </div> </template> diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue index 137dfca68d6d0873b87c6ee7314f3be46d20f79a..4b9c98135ec60030d09a112b15a3be39c2887ef9 100644 --- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue +++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue @@ -174,7 +174,7 @@ export default { <div class="gl-display-flex gl-flex-wrap"> <pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" /> <gl-button - class="gl-ml-3" + class="gl-mt-2 gl-md-mt-0" category="secondary" variant="confirm" :href="status.detailsPath" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue index f1edc3d4be8d73e382c6a51163ce079688906f2e..05cb2ebb769e6a0f8f0d62653f9ced09d4207229 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue @@ -1,60 +1,32 @@ <script> -import { GlIcon } from '@gitlab/ui'; -import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; +import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; /** * Renders the pipeline mini graph. */ export default { components: { - GlIcon, - PipelineStages, - LinkedPipelinesMiniList: () => - import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), + PipelineStage, }, - arrowStyles: [ - 'arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 gl-vertical-align-middle!', - ], props: { - downstreamPipelines: { + stages: { type: Array, - required: false, - default: () => [], + required: true, }, - isMergeTrain: { + updateDropdown: { type: Boolean, required: false, default: false, }, - pipelinePath: { - type: String, - required: false, - default: '', - }, - stages: { - type: Array, - required: true, - default: () => [], - }, stagesClass: { type: [Array, Object, String], required: false, default: '', }, - updateDropdown: { + isMergeTrain: { type: Boolean, required: false, default: false, }, - upstreamPipeline: { - type: Object, - required: false, - default: () => {}, - }, - }, - computed: { - hasDownstreamPipelines() { - return Boolean(this.downstreamPipelines.length); - }, }, methods: { onPipelineActionRequestComplete() { @@ -64,39 +36,19 @@ export default { }; </script> <template> - <div class="stage-cell" data-testid="pipeline-mini-graph"> - <linked-pipelines-mini-list - v-if="upstreamPipeline" - :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ - upstreamPipeline, - ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - data-testid="pipeline-mini-graph-upstream" - /> - <gl-icon - v-if="upstreamPipeline" - :class="$options.arrowStyles" - name="long-arrow" - data-testid="upstream-arrow-icon" - /> - <pipeline-stages - :is-merge-train="isMergeTrain" - :stages="stages" - :update-dropdown="updateDropdown" - :stages-class="stagesClass" - data-testid="pipeline-stages" - @pipelineActionRequestComplete="onPipelineActionRequestComplete" - /> - <gl-icon - v-if="hasDownstreamPipelines" - :class="$options.arrowStyles" - name="long-arrow" - data-testid="downstream-arrow-icon" - /> - <linked-pipelines-mini-list - v-if="hasDownstreamPipelines" - :triggered="downstreamPipelines" - :pipeline-path="pipelinePath" - data-testid="pipeline-mini-graph-downstream" - /> + <div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle"> + <div + v-for="stage in stages" + :key="stage.name" + :class="stagesClass" + class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container" + > + <pipeline-stage + :stage="stage" + :update-dropdown="updateDropdown" + :is-merge-train="isMergeTrain" + @pipelineActionRequestComplete="onPipelineActionRequestComplete" + /> + </div> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue deleted file mode 100644 index f1923e94a477a3d282d159a8665ac7594988dde4..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue +++ /dev/null @@ -1,54 +0,0 @@ -<script> -import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; -/** - * Renders the pipeline stages portion of the pipeline mini graph. - */ -export default { - components: { - PipelineStage, - }, - props: { - stages: { - type: Array, - required: true, - }, - updateDropdown: { - type: Boolean, - required: false, - default: false, - }, - stagesClass: { - type: [Array, Object, String], - required: false, - default: '', - }, - isMergeTrain: { - type: Boolean, - required: false, - default: false, - }, - }, - methods: { - onPipelineActionRequestComplete() { - this.$emit('pipelineActionRequestComplete'); - }, - }, -}; -</script> -<template> - <div data-testid="pipeline-stages" class="gl-display-inline gl-vertical-align-middle"> - <div - v-for="stage in stages" - :key="stage.name" - :class="stagesClass" - class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container" - > - <pipeline-stage - :stage="stage" - :update-dropdown="updateDropdown" - :is-merge-train="isMergeTrain" - @pipelineActionRequestComplete="onPipelineActionRequestComplete" - /> - </div> - </div> -</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue index 4046ee69428dec3f553080abfc9d67d1202ee6d7..53da98434b0d8c2a0bb7efbf3bf2188a059889c0 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue @@ -17,6 +17,8 @@ const DEFAULT_TH_CLASSES = export default { components: { GlTableLite, + LinkedPipelinesMiniList: () => + import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), PipelineMiniGraph, PipelineOperations, PipelinesStatusBadge, @@ -167,14 +169,29 @@ export default { </template> <template #cell(stages)="{ item }"> - <pipeline-mini-graph - :downstream-pipelines="item.triggered" - :pipeline-path="item.path" - :stages="item.details.stages" - :update-dropdown="updateGraphDropdown" - :upstream-pipeline="item.triggered_by" - @pipelineActionRequestComplete="onPipelineActionRequestComplete" - /> + <div class="stage-cell"> + <!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 --> + <div></div> + <linked-pipelines-mini-list + v-if="item.triggered_by" + :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ + item.triggered_by, + ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" + data-testid="mini-graph-upstream" + /> + <pipeline-mini-graph + v-if="item.details && item.details.stages && item.details.stages.length > 0" + :stages="item.details.stages" + :update-dropdown="updateGraphDropdown" + @pipelineActionRequestComplete="onPipelineActionRequestComplete" + /> + <linked-pipelines-mini-list + v-if="item.triggered.length" + :triggered="item.triggered" + :pipeline-path="item.path" + data-testid="mini-graph-downstream" + /> + </div> </template> <template #cell(actions)="{ item }"> diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue index a4044106a531b2d85cee27c0dd982f69b8b18ed5..1cdf26b76b752e9f88ee7efdc4cea1f6b3e69b67 100644 --- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue @@ -2,11 +2,11 @@ import { GlLoadingIcon } from '@gitlab/ui'; import createFlash from '~/flash'; import { __ } from '~/locale'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { getQueryHeaders, toggleQueryPollingByVisibility, } from '~/pipelines/components/graph/utils'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { formatStages } from '../utils'; import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql'; import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql'; @@ -21,6 +21,8 @@ export default { components: { GlLoadingIcon, PipelineMiniGraph, + LinkedPipelinesMiniList: () => + import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, inject: { fullPath: { @@ -90,12 +92,12 @@ export default { }; }, computed: { + hasDownstream() { + return this.pipeline?.downstream?.nodes.length > 0; + }, downstreamPipelines() { return this.pipeline?.downstream?.nodes; }, - pipelinePath() { - return this.pipeline?.path ?? ''; - }, upstreamPipeline() { return this.pipeline?.upstream; }, @@ -126,13 +128,23 @@ export default { <template> <div class="gl-pt-2"> <gl-loading-icon v-if="$apollo.queries.pipeline.loading" /> - <pipeline-mini-graph - v-else - data-testid="commit-box-pipeline-mini-graph" - :downstream-pipelines="downstreamPipelines" - :pipeline-path="pipelinePath" - :stages="formattedStages" - :upstream-pipeline="upstreamPipeline" - /> + <div v-else class="gl-align-items-center gl-display-flex"> + <linked-pipelines-mini-list + v-if="upstreamPipeline" + :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ + upstreamPipeline, + ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" + data-testid="commit-box-mini-graph-upstream" + /> + + <pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" /> + + <linked-pipelines-mini-list + v-if="hasDownstream" + :triggered="downstreamPipelines" + :pipeline-path="pipeline.path" + data-testid="commit-box-mini-graph-downstream" + /> + </div> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index 5ecf49b51be8112b85ccd5637d5927a0f279819b..1e1a20494145715440559ba2e9c5815b47ac52b5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -11,8 +11,8 @@ import { } from '@gitlab/ui'; import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline'; import { s__, n__ } from '~/locale'; -import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; +import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; @@ -31,6 +31,8 @@ export default { PipelineMiniGraph, TimeAgoTooltip, TooltipOnTruncate, + LinkedPipelinesMiniList: () => + import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, directives: { GlTooltip: GlTooltipDirective, @@ -274,15 +276,17 @@ export default { </div> </div> <div> - <span class="gl-align-items-center gl-display-inline-flex"> - <pipeline-mini-graph - v-if="pipeline.details.stages" - :downstream-pipelines="triggered" - :is-merge-train="isMergeTrain" - :stages="pipeline.details.stages" - :upstream-pipeline="triggeredBy[0]" - stages-class="mr-widget-pipeline-stages" - /> + <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph"> + <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell"> + <linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" /> + <pipeline-mini-graph + v-if="hasStages" + stages-class="mr-widget-pipeline-stages" + :stages="pipeline.details.stages" + :is-merge-train="isMergeTrain" + /> + </span> + <linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" /> <pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" /> </span> </div> diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss index 269afd01615e32e5a7f6f178b9cbeaecb8b776e9..14873c54cd77d8f6cddbf0ed82b76811ef1c737c 100644 --- a/app/assets/stylesheets/page_bundles/merge_requests.scss +++ b/app/assets/stylesheets/page_bundles/merge_requests.scss @@ -400,6 +400,12 @@ $tabs-holder-z-index: 250; display: block; } + .mr-widget-pipeline-graph { + .dropdown-menu { + z-index: $zindex-dropdown-menu; + } + } + .normal { flex: 1; flex-basis: auto; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 19318d8773127c63d7052430852adda1b6beab41..c96d8ecc782ee805b286ed6c1c325846f8f47688 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,6 +33,12 @@ height: 22px; } } + + .mr-widget-pipeline-graph { + .dropdown-menu { + margin-top: 11px; + } + } } .branch-info .commit-icon { diff --git a/ee/app/assets/javascripts/vue_shared/components/linked_pipelines_mini_list.vue b/ee/app/assets/javascripts/vue_shared/components/linked_pipelines_mini_list.vue index 4d180dfcd824905ca818d51914257c944e64134a..4ebe9539411cb469eae60a8908ef311b9f16729e 100644 --- a/ee/app/assets/javascripts/vue_shared/components/linked_pipelines_mini_list.vue +++ b/ee/app/assets/javascripts/vue_shared/components/linked_pipelines_mini_list.vue @@ -1,18 +1,20 @@ <script> -import { GlTooltipDirective } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { sprintf, s__ } from '~/locale'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import { accessValue } from '../accessors/linked_pipelines_accessors'; -/** - * Renders the upstream/downstream portions of the pipeline mini graph. - */ + export default { directives: { GlTooltip: GlTooltipDirective, }, components: { CiIcon, + GlIcon, }, + arrowStyles: [ + 'arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 gl-vertical-align-middle!', + ], inject: { dataMethod: { default: 'rest', @@ -99,6 +101,8 @@ export default { }" class="linked-pipeline-mini-list gl-display-inline gl-vertical-align-middle" > + <gl-icon v-if="isDownstream" :class="$options.arrowStyles" name="long-arrow" /> + <a v-for="pipeline in linkedPipelinesTrimmed" :key="pipeline.id" @@ -128,5 +132,7 @@ export default { > {{ counterLabel }} </a> + + <gl-icon v-if="isUpstream" :class="$options.arrowStyles" name="long-arrow" /> </span> </template> diff --git a/ee/spec/frontend/commit_box/components/commit_box_pipeline_mini_graph_spec.js b/ee/spec/frontend/commit_box/components/commit_box_pipeline_mini_graph_spec.js index 4dd6e9e54867aa553ddfd668dc71f05424f4a384..ceb43768657a0bbe134c0e71c932d7093d524d2d 100644 --- a/ee/spec/frontend/commit_box/components/commit_box_pipeline_mini_graph_spec.js +++ b/ee/spec/frontend/commit_box/components/commit_box_pipeline_mini_graph_spec.js @@ -7,7 +7,6 @@ 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 PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { COMMIT_BOX_POLL_INTERVAL } from '~/projects/commit_box/info/constants'; 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'; @@ -38,7 +37,9 @@ describe('Commit box pipeline mini graph', () => { const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph'); + const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); + const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); const advanceToNextFetch = () => { jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL); @@ -79,25 +80,18 @@ describe('Commit box pipeline mini graph', () => { createComponent(); expect(findLoadingIcon().exists()).toBe(true); - expect(findPipelineMiniGraph().exists()).toBe(false); + expect(findMiniGraph().exists()).toBe(false); }); }); describe('loaded state', () => { - const samplePipeline = { - id: expect.any(String), - path: expect.any(String), - project: expect.any(Object), - detailedStatus: expect.any(Object), - }; - it('should not display loading state after the query is resolved', async () => { createComponent(); await waitForPromises(); expect(findLoadingIcon().exists()).toBe(false); - expect(findPipelineMiniGraph().exists()).toBe(true); + expect(findMiniGraph().exists()).toBe(true); }); it('should pass the pipeline path prop for the counter badge', async () => { @@ -106,45 +100,24 @@ describe('Commit box pipeline mini graph', () => { await waitForPromises(); const expectedPath = mockDownstreamQueryResponse.data.project.pipeline.path; - const pipelinePath = findPipelineMiniGraph().props('pipelinePath'); - - expect(pipelinePath).toBe(expectedPath); - }); - - it('should render a downstream pipeline only', async () => { - createComponent(downstreamHandler); - - await waitForPromises(); - - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - - expect(downstreamPipelines).toEqual(expect.any(Array)); - expect(upstreamPipeline).toEqual(null); - }); - - it('should render an upstream pipeline only', async () => { - createComponent(upstreamHandler); - - await waitForPromises(); - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - - expect(upstreamPipeline).toEqual(samplePipeline); - expect(downstreamPipelines).toHaveLength(0); + expect(findDownstream().props('pipelinePath')).toBe(expectedPath); }); - it('should render downstream and upstream pipelines', async () => { - createComponent(upstreamDownstreamHandler); - - await waitForPromises(); + describe.each` + handler | downstreamRenders | upstreamRenders + ${downstreamHandler} | ${true} | ${false} + ${upstreamHandler} | ${false} | ${true} + ${upstreamDownstreamHandler} | ${true} | ${true} + `('given a linked pipeline', ({ handler, downstreamRenders, upstreamRenders }) => { + it('should render the correct linked pipelines', async () => { + createComponent(handler); - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); + await waitForPromises(); - expect(upstreamPipeline).toEqual(samplePipeline); - expect(downstreamPipelines).toEqual(expect.arrayContaining([samplePipeline])); + expect(findDownstream().exists()).toBe(downstreamRenders); + expect(findUpstream().exists()).toBe(upstreamRenders); + }); }); it('formatted stages should be passed to the pipeline mini graph', async () => { @@ -166,7 +139,7 @@ describe('Commit box pipeline mini graph', () => { await waitForPromises(); - expect(findPipelineMiniGraph().props('stages')).toEqual(expectedStages); + expect(findMiniGraph().props('stages')).toEqual(expectedStages); }); }); diff --git a/ee/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/ee/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js index c6700a74e34fa164c76527de9ad4ac3d493aab5f..cd4910e77411ebea0bb74b1f9931e0475bf12b13 100644 --- a/ee/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js +++ b/ee/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js @@ -1,9 +1,9 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import waitForPromises from 'helpers/wait_for_promises'; import createMockApollo from 'helpers/mock_apollo_helper'; import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql'; import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data'; @@ -39,7 +39,9 @@ describe('Pipeline Status', () => { }); }; - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findUpstream = () => wrapper.find('[data-testid="pipeline-editor-mini-graph-upstream"]'); + const findDownstream = () => + wrapper.find('[data-testid="pipeline-editor-mini-graph-downstream"]'); beforeEach(() => { mockLinkedPipelinesQuery = jest.fn(); @@ -50,31 +52,33 @@ describe('Pipeline Status', () => { wrapper.destroy(); }); - describe('when querying pipeline stages', () => { - describe('when query returns data', () => { + describe('when querying upstream and downstream pipelines', () => { + describe('when query succeeds', () => { beforeEach(() => { mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines()); createComponentWithApollo(); }); - describe('pipeline mini graph rendering based on given data', () => { - it('renders pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); - }); - }); - }); - - describe('when query returns no data', () => { - beforeEach(() => { - mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines()); - const hasStages = false; - createComponentWithApollo(hasStages); - }); + describe('linked pipeline rendering based on given data', () => { + it.each` + hasDownstream | hasUpstream | downstreamRenderAction | upstreamRenderAction + ${true} | ${true} | ${'renders'} | ${'renders'} + ${true} | ${false} | ${'renders'} | ${'hides'} + ${false} | ${true} | ${'hides'} | ${'renders'} + ${false} | ${false} | ${'hides'} | ${'hides'} + `( + '$downstreamRenderAction downstream and $upstreamRenderAction upstream', + async ({ hasDownstream, hasUpstream }) => { + mockLinkedPipelinesQuery.mockResolvedValue( + mockLinkedPipelines({ hasDownstream, hasUpstream }), + ); + createComponentWithApollo(); + await waitForPromises(); - describe('pipeline mini graph rendering based on given data', () => { - it('does not render pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(false); - }); + expect(findUpstream().exists()).toBe(hasUpstream); + expect(findDownstream().exists()).toBe(hasDownstream); + }, + ); }); }); }); diff --git a/ee/spec/frontend/pipelines/pipelines_table_spec.js b/ee/spec/frontend/pipelines/pipelines_table_spec.js index b17ab954c568be52bbda044c91f0ce8f7ab6cc5a..5ce7f28dee1939be8e255ba0e6045f421c4d5562 100644 --- a/ee/spec/frontend/pipelines/pipelines_table_spec.js +++ b/ee/spec/frontend/pipelines/pipelines_table_spec.js @@ -2,7 +2,6 @@ import { mount } from '@vue/test-utils'; import fixture from 'test_fixtures/pipelines/pipelines.json'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import PipelinesTable from '~/pipelines/components/pipelines_list/pipelines_table.vue'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { PipelineKeyOptions } from '~/pipelines/constants'; import { triggeredBy, triggered } from './mock_data'; @@ -36,7 +35,8 @@ describe('Pipelines Table', () => { ); }; - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findUpstream = () => wrapper.findByTestId('mini-graph-upstream'); + const findDownstream = () => wrapper.findByTestId('mini-graph-downstream'); beforeEach(() => { pipeline = createMockPipeline(); @@ -47,19 +47,6 @@ describe('Pipelines Table', () => { }); describe('Pipelines Table', () => { - describe('pipeline mini graph', () => { - beforeEach(() => { - pipeline = createMockPipeline(); - pipeline.triggered_by = triggeredBy; - - createComponent({ pipelines: [pipeline] }); - }); - - it('should render a pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); - }); - }); - describe('upstream linked pipelines', () => { beforeEach(() => { pipeline = createMockPipeline(); @@ -69,17 +56,16 @@ describe('Pipelines Table', () => { }); it('should render only a upstream pipeline', () => { - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - - expect(upstreamPipeline).toEqual(expect.any(Object)); - expect(downstreamPipelines).toHaveLength(0); + expect(findUpstream().exists()).toBe(true); + expect(findDownstream().exists()).toBe(false); }); - it('should pass an object of the correct data to the linked pipeline component', () => { - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); + it('should pass an array of the correct data to the linked pipeline component', () => { + const triggeredByProps = findUpstream().props('triggeredBy'); - expect(upstreamPipeline).toBe(triggeredBy); + expect(triggeredByProps).toEqual(expect.any(Array)); + expect(triggeredByProps).toHaveLength(1); + expect(triggeredByProps[0]).toBe(triggeredBy); }); }); @@ -92,22 +78,12 @@ describe('Pipelines Table', () => { }); it('should pass the pipeline path prop for the counter badge', () => { - const pipelinePath = findPipelineMiniGraph().props('pipelinePath'); - expect(pipelinePath).toBe(pipeline.path); + expect(findDownstream().props('pipelinePath')).toBe(pipeline.path); }); it('should render only a downstream pipeline', () => { - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - - expect(downstreamPipelines).toEqual(expect.any(Array)); - expect(upstreamPipeline).toEqual(null); - }); - - it('should pass an array of the correct data to the linked pipeline component', () => { - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - - expect(downstreamPipelines).toEqual(triggered); + expect(findDownstream().exists()).toBe(true); + expect(findUpstream().exists()).toBe(false); }); }); @@ -121,11 +97,8 @@ describe('Pipelines Table', () => { }); it('should render both downstream and upstream pipelines', () => { - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - - expect(downstreamPipelines).toEqual(triggered); - expect(upstreamPipeline).toEqual(triggeredBy); + expect(findDownstream().exists()).toBe(true); + expect(findUpstream().exists()).toBe(true); }); }); }); diff --git a/ee/spec/frontend/vue_mr_widget/mr_widget_pipeline_spec.js b/ee/spec/frontend/vue_mr_widget/mr_widget_pipeline_spec.js index 7b87df4d5cf3a55b7564e51f90697ecf44d9e5aa..8c35938fcb9c875363572cfa4ec64b53dbc6c925 100644 --- a/ee/spec/frontend/vue_mr_widget/mr_widget_pipeline_spec.js +++ b/ee/spec/frontend/vue_mr_widget/mr_widget_pipeline_spec.js @@ -1,14 +1,14 @@ import { shallowMount } from '@vue/test-utils'; +import LinkedPipelinesMiniList from 'ee/vue_shared/components/linked_pipelines_mini_list.vue'; import mockData from 'ee_jest/vue_mr_widget/mock_data'; import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import mockLinkedPipelines from '../vue_shared/components/linked_pipelines_mock_data'; describe('MRWidgetPipeline', () => { let wrapper; const findPipelineInfoContainer = () => wrapper.find('[data-testid="pipeline-info-container"'); - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findPipelinesMiniList = () => wrapper.findComponent(LinkedPipelinesMiniList); const createWrapper = (props) => { wrapper = shallowMount(MrWidgetPipeline, { @@ -75,14 +75,20 @@ describe('MRWidgetPipeline', () => { createWrapper({ pipeline }); }); - it('should render the pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); + it('should render the linked pipelines mini list', () => { + expect(findPipelinesMiniList().exists()).toBe(true); }); - it('should send upstream pipeline', () => { - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); + it('should render the linked pipelines mini list as an upstream list', () => { + expect(findPipelinesMiniList().classes('is-upstream')).toBe(true); + }); + + it('should add a single triggeredBy into an array', () => { + const triggeredBy = findPipelinesMiniList().props('triggeredBy'); - expect(upstreamPipeline).toBe(mockLinkedPipelines.triggered_by); + expect(triggeredBy).toEqual(expect.any(Array)); + expect(triggeredBy).toHaveLength(1); + expect(triggeredBy[0]).toBe(mockLinkedPipelines.triggered_by); }); }); @@ -93,14 +99,18 @@ describe('MRWidgetPipeline', () => { createWrapper({ pipeline }); }); - it('should render the pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); + it('should render the linked pipelines mini list', () => { + expect(findPipelinesMiniList().exists()).toBe(true); }); it('should render the linked pipelines mini list as a downstream list', () => { - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); + expect(findPipelinesMiniList().classes('is-downstream')).toBe(true); + }); + + it('should pass the triggered pipelines', () => { + const triggered = findPipelinesMiniList().props('triggered'); - expect(downstreamPipelines).toBe(mockLinkedPipelines.triggered); + expect(triggered).toBe(mockLinkedPipelines.triggered); }); }); }); diff --git a/ee/spec/frontend/vue_shared/components/linked_pipelines_mini_list_spec.js b/ee/spec/frontend/vue_shared/components/linked_pipelines_mini_list_spec.js index 8896faf5c34b28c5340da867cd378949d47262e4..3cebe451ceb5224a281742cd2a5c88bfe897b920 100644 --- a/ee/spec/frontend/vue_shared/components/linked_pipelines_mini_list_spec.js +++ b/ee/spec/frontend/vue_shared/components/linked_pipelines_mini_list_spec.js @@ -7,6 +7,7 @@ import mockData from './linked_pipelines_mock_data'; describe('Linked pipeline mini list', () => { let wrapper; + const findArrowIcon = () => wrapper.find('[data-testid="long-arrow-icon"]'); const findCiIcon = () => wrapper.findComponent(CiIcon); const findCiIcons = () => wrapper.findAllComponents(CiIcon); const findLinkedPipelineCounter = () => wrapper.find('[data-testid="linked-pipeline-counter"]'); @@ -72,6 +73,13 @@ describe('Linked pipeline mini list', () => { expect(findCiIcon().classes('ci-status-icon-running')).toBe(true); }); + it('should render an arrow icon', () => { + expect(findArrowIcon().exists()).toBe(true); + + expect(findArrowIcon().props('name')).toBe('long-arrow'); + expect(findArrowIcon().classes('arrow-icon')).toBe(true); + }); + it('should have an activated tooltip', () => { expect(findLinkedPipelineMiniItem().exists()).toBe(true); const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip'); @@ -121,6 +129,13 @@ describe('Linked pipeline mini list', () => { expect(findCiIcon().classes('ci-status-icon-running')).toBe(true); }); + it('should render an arrow icon', () => { + expect(findArrowIcon().exists()).toBe(true); + + expect(findArrowIcon().props('name')).toBe('long-arrow'); + expect(findArrowIcon().classes('arrow-icon')).toBe(true); + }); + it('should have an activated tooltip', () => { expect(findLinkedPipelineMiniItem().exists()).toBe(true); const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip'); diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index 4740f6e19fe57248dab609c1367ba85fc58b055e..e472cff38cefb34918d88b1793d1ca2995b9626f 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -27,7 +27,7 @@ end it 'displays a mini pipeline graph' do - expect(page).to have_selector('[data-testid="commit-box-pipeline-mini-graph"]') + expect(page).to have_selector('[data-testid="commit-box-mini-graph"]') first('[data-testid="mini-pipeline-graph-dropdown"]').click diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js index ab5055de5e3fd1c02d7f16f58501ee623bbbb0ff..b1c8ba48475f7358a18a6227f5e40e194d6993cd 100644 --- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js +++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js @@ -6,7 +6,6 @@ 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 PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; 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'; @@ -18,7 +17,9 @@ Vue.use(VueApollo); describe('Commit box pipeline mini graph', () => { let wrapper; - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph'); + const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); + const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse); @@ -50,16 +51,13 @@ describe('Commit box pipeline mini graph', () => { await createComponent(); }); - it('should display the pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); + it('should display the mini pipeine graph', () => { + expect(findMiniGraph().exists()).toBe(true); }); it('should not display linked pipelines', () => { - const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); - const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); - - expect(downstreamPipelines).toHaveLength(0); - expect(upstreamPipeline).toEqual(undefined); + expect(findUpstream().exists()).toBe(false); + expect(findDownstream().exists()).toBe(false); }); }); diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js deleted file mode 100644 index 93eb18c90cf16b31852cc411200c14b01347c829..0000000000000000000000000000000000000000 --- a/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js +++ /dev/null @@ -1,109 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; -import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql'; -import { PIPELINE_FAILURE } from '~/pipeline_editor/constants'; -import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data'; - -Vue.use(VueApollo); - -describe('Pipeline Status', () => { - let wrapper; - let mockApollo; - let mockLinkedPipelinesQuery; - - const createComponent = ({ hasStages = true, options } = {}) => { - wrapper = shallowMount(PipelineEditorMiniGraph, { - provide: { - dataMethod: 'graphql', - projectFullPath: mockProjectFullPath, - }, - propsData: { - pipeline: mockProjectPipeline({ hasStages }).pipeline, - }, - ...options, - }); - }; - - const createComponentWithApollo = (hasStages = true) => { - const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]]; - mockApollo = createMockApollo(handlers); - - createComponent({ - hasStages, - options: { - apolloProvider: mockApollo, - }, - }); - }; - - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); - - beforeEach(() => { - mockLinkedPipelinesQuery = jest.fn(); - }); - - afterEach(() => { - mockLinkedPipelinesQuery.mockReset(); - wrapper.destroy(); - }); - - describe('when there are stages', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); - }); - }); - - describe('when there are no stages', () => { - beforeEach(() => { - createComponent({ hasStages: false }); - }); - - it('does not render pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(false); - }); - }); - - describe('when querying upstream and downstream pipelines', () => { - describe('when query succeeds', () => { - beforeEach(() => { - mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines()); - createComponentWithApollo(); - }); - - it('should call the query with the correct variables', () => { - expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1); - expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({ - fullPath: mockProjectFullPath, - iid: mockProjectPipeline().pipeline.iid, - }); - }); - }); - - describe('when query fails', () => { - beforeEach(async () => { - mockLinkedPipelinesQuery.mockRejectedValue(new Error()); - createComponentWithApollo(); - await waitForPromises(); - }); - - it('should emit an error event when query fails', async () => { - expect(wrapper.emitted('showError')).toHaveLength(1); - expect(wrapper.emitted('showError')[0]).toEqual([ - { - type: PIPELINE_FAILURE, - reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError], - }, - ]); - }); - }); - }); -}); diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_stages_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js similarity index 92% rename from spec/frontend/pipelines/components/pipelines_list/pipeline_stages_spec.js rename to spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js index 1e31d8a62ffd997941a3d22efc96a66550b8b68e..1cb43c199aa5f70e461dc53ecef07dc4f462fbb0 100644 --- a/spec/frontend/pipelines/components/pipelines_list/pipeline_stages_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js @@ -1,18 +1,18 @@ import { shallowMount } from '@vue/test-utils'; import { pipelines } from 'test_fixtures/pipelines/pipelines.json'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; -import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; const mockStages = pipelines[0].details.stages; -describe('Pipeline Stages', () => { +describe('Pipeline Mini Graph', () => { let wrapper; const findPipelineStages = () => wrapper.findAll(PipelineStage); const findPipelineStagesAt = (i) => findPipelineStages().at(i); const createComponent = (props = {}) => { - wrapper = shallowMount(PipelineStages, { + wrapper = shallowMount(PipelineMiniGraph, { propsData: { stages: mockStages, ...props, diff --git a/spec/frontend/pipelines/linked_pipelines_mock_data.js b/spec/frontend/pipelines/linked_pipelines_mock_data.js deleted file mode 100644 index 117c7f2ae529fc75849d75e27361e40ac9f3a712..0000000000000000000000000000000000000000 --- a/spec/frontend/pipelines/linked_pipelines_mock_data.js +++ /dev/null @@ -1,407 +0,0 @@ -export default { - triggered_by: { - id: 129, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/129', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/129', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: '7-5-stable', - path: '/gitlab-org/gitlab-foss/commits/7-5-stable', - tag: false, - branch: true, - }, - commit: { - id: '23433d4d8b20d7e45c103d0b6048faad38a130ab', - short_id: '23433d4d', - title: 'Version 7.5.0.rc1', - created_at: '2014-11-17T15:44:14.000+01:00', - parent_ids: ['30ac909f30f58d319b42ed1537664483894b18cd'], - message: 'Version 7.5.0.rc1\n', - author_name: 'Jacob Vosmaer', - author_email: 'contact@jacobvosmaer.nl', - authored_date: '2014-11-17T15:44:14.000+01:00', - committer_name: 'Jacob Vosmaer', - committer_email: 'contact@jacobvosmaer.nl', - committed_date: '2014-11-17T15:44:14.000+01:00', - author_gravatar_url: - 'http://www.gravatar.com/avatar/e66d11c0eedf8c07b3b18fca46599807?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab', - commit_path: '/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/129/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/129/cancel', - created_at: '2017-05-24T14:46:20.090Z', - updated_at: '2017-05-24T14:46:29.906Z', - }, - triggered: [ - { - id: 132, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/132', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/132', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - short_id: 'b9d58c4c', - title: 'getting user keys publically through http without any authentication, the github…', - created_at: '2013-10-03T12:50:33.000+05:30', - parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], - message: - 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n', - author_name: 'devaroop', - author_email: 'devaroop123@yahoo.co.in', - authored_date: '2013-10-02T20:39:29.000+05:30', - committer_name: 'devaroop', - committer_email: 'devaroop123@yahoo.co.in', - committed_date: '2013-10-03T12:50:33.000+05:30', - author_gravatar_url: - 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel', - created_at: '2017-05-24T14:46:24.644Z', - updated_at: '2017-05-24T14:48:55.226Z', - }, - { - id: 133, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/133', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/133', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b', - short_id: 'b6bd4856', - title: 'getting user keys publically through http without any authentication, the github…', - created_at: '2013-10-02T20:39:29.000+05:30', - parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], - message: - 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n', - author_name: 'devaroop', - author_email: 'devaroop123@yahoo.co.in', - authored_date: '2013-10-02T20:39:29.000+05:30', - committer_name: 'devaroop', - committer_email: 'devaroop123@yahoo.co.in', - committed_date: '2013-10-02T20:39:29.000+05:30', - author_gravatar_url: - 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', - commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel', - created_at: '2017-05-24T14:46:24.648Z', - updated_at: '2017-05-24T14:48:59.673Z', - }, - { - id: 130, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/130', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/130', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f', - short_id: '6d7ced4a', - title: 'Whitespace fixes to patch', - created_at: '2013-10-08T13:53:22.000-05:00', - parent_ids: ['1875141a963a4238bda29011d8f7105839485253'], - message: 'Whitespace fixes to patch\n', - author_name: 'Dale Hamel', - author_email: 'dale.hamel@srvthe.net', - authored_date: '2013-10-08T13:53:22.000-05:00', - committer_name: 'Dale Hamel', - committer_email: 'dale.hamel@invenia.ca', - committed_date: '2013-10-08T13:53:22.000-05:00', - author_gravatar_url: - 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', - commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel', - created_at: '2017-05-24T14:46:24.630Z', - updated_at: '2017-05-24T14:49:45.091Z', - }, - { - id: 131, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/132', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/132', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - short_id: 'b9d58c4c', - title: 'getting user keys publically through http without any authentication, the github…', - created_at: '2013-10-03T12:50:33.000+05:30', - parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], - message: - 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n', - author_name: 'devaroop', - author_email: 'devaroop123@yahoo.co.in', - authored_date: '2013-10-02T20:39:29.000+05:30', - committer_name: 'devaroop', - committer_email: 'devaroop123@yahoo.co.in', - committed_date: '2013-10-03T12:50:33.000+05:30', - author_gravatar_url: - 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel', - created_at: '2017-05-24T14:46:24.644Z', - updated_at: '2017-05-24T14:48:55.226Z', - }, - { - id: 134, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/133', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/133', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b', - short_id: 'b6bd4856', - title: 'getting user keys publically through http without any authentication, the github…', - created_at: '2013-10-02T20:39:29.000+05:30', - parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], - message: - 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n', - author_name: 'devaroop', - author_email: 'devaroop123@yahoo.co.in', - authored_date: '2013-10-02T20:39:29.000+05:30', - committer_name: 'devaroop', - committer_email: 'devaroop123@yahoo.co.in', - committed_date: '2013-10-02T20:39:29.000+05:30', - author_gravatar_url: - 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', - commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel', - created_at: '2017-05-24T14:46:24.648Z', - updated_at: '2017-05-24T14:48:59.673Z', - }, - { - id: 135, - active: true, - path: '/gitlab-org/gitlab-foss/-/pipelines/130', - project: { - name: 'GitLabCE', - }, - details: { - status: { - icon: 'status_running', - text: 'running', - label: 'running', - group: 'running', - has_details: true, - details_path: '/gitlab-org/gitlab-foss/-/pipelines/130', - favicon: - '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', - }, - }, - flags: { - latest: false, - triggered: false, - stuck: false, - yaml_errors: false, - retryable: true, - cancelable: true, - }, - ref: { - name: 'crowd', - path: '/gitlab-org/gitlab-foss/commits/crowd', - tag: false, - branch: true, - }, - commit: { - id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f', - short_id: '6d7ced4a', - title: 'Whitespace fixes to patch', - created_at: '2013-10-08T13:53:22.000-05:00', - parent_ids: ['1875141a963a4238bda29011d8f7105839485253'], - message: 'Whitespace fixes to patch\n', - author_name: 'Dale Hamel', - author_email: 'dale.hamel@srvthe.net', - authored_date: '2013-10-08T13:53:22.000-05:00', - committer_name: 'Dale Hamel', - committer_email: 'dale.hamel@invenia.ca', - committed_date: '2013-10-08T13:53:22.000-05:00', - author_gravatar_url: - 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon', - commit_url: - 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', - commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', - }, - retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry', - cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel', - created_at: '2017-05-24T14:46:24.630Z', - updated_at: '2017-05-24T14:49:45.091Z', - }, - ], -}; diff --git a/spec/frontend/pipelines/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/pipeline_mini_graph_spec.js deleted file mode 100644 index 81aa97ce13fe3db1fee771e1fe52a141c7db2e23..0000000000000000000000000000000000000000 --- a/spec/frontend/pipelines/pipeline_mini_graph_spec.js +++ /dev/null @@ -1,149 +0,0 @@ -import { mount } from '@vue/test-utils'; -import { pipelines } from 'test_fixtures/pipelines/pipelines.json'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; -import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; -import mockLinkedPipelines from './linked_pipelines_mock_data'; - -const mockStages = pipelines[0].details.stages; - -describe('Pipeline Mini Graph', () => { - let wrapper; - - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); - const findPipelineStages = () => wrapper.findComponent(PipelineStages); - - const findLinkedPipelineUpstream = () => - wrapper.findComponent('[data-testid="pipeline-mini-graph-upstream"]'); - const findLinkedPipelineDownstream = () => - wrapper.findComponent('[data-testid="pipeline-mini-graph-downstream"]'); - const findDownstreamArrowIcon = () => wrapper.find('[data-testid="downstream-arrow-icon"]'); - const findUpstreamArrowIcon = () => wrapper.find('[data-testid="upstream-arrow-icon"]'); - - const createComponent = (props = {}) => { - wrapper = mount(PipelineMiniGraph, { - propsData: { - stages: mockStages, - ...props, - }, - }); - }; - - describe('rendered state without upstream or downstream pipelines', () => { - beforeEach(() => { - createComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('should render the pipeline stages', () => { - expect(findPipelineStages().exists()).toBe(true); - }); - - it('should have the correct props', () => { - expect(findPipelineMiniGraph().props()).toMatchObject({ - downstreamPipelines: [], - isMergeTrain: false, - pipelinePath: '', - stages: expect.any(Array), - stagesClass: '', - updateDropdown: false, - upstreamPipeline: undefined, - }); - }); - - it('should have no linked pipelines', () => { - expect(findLinkedPipelineDownstream().exists()).toBe(false); - expect(findLinkedPipelineUpstream().exists()).toBe(false); - }); - - it('should not render arrow icons', () => { - expect(findUpstreamArrowIcon().exists()).toBe(false); - expect(findDownstreamArrowIcon().exists()).toBe(false); - }); - - it('triggers events in "action request complete"', () => { - createComponent(); - - findPipelineMiniGraph(0).vm.$emit('pipelineActionRequestComplete'); - findPipelineMiniGraph(1).vm.$emit('pipelineActionRequestComplete'); - - expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(2); - }); - }); - - describe('rendered state with upstream pipeline', () => { - beforeEach(() => { - createComponent({ - upstreamPipeline: mockLinkedPipelines.triggered_by, - }); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('should have the correct props', () => { - expect(findPipelineMiniGraph().props()).toMatchObject({ - downstreamPipelines: [], - isMergeTrain: false, - pipelinePath: '', - stages: expect.any(Array), - stagesClass: '', - updateDropdown: false, - upstreamPipeline: expect.any(Object), - }); - }); - - it('should render the upstream linked pipelines mini list only', () => { - expect(findLinkedPipelineUpstream().exists()).toBe(true); - expect(findLinkedPipelineDownstream().exists()).toBe(false); - }); - - it('should render an upstream arrow icon only', () => { - expect(findDownstreamArrowIcon().exists()).toBe(false); - expect(findUpstreamArrowIcon().exists()).toBe(true); - expect(findUpstreamArrowIcon().props('name')).toBe('long-arrow'); - }); - }); - - describe('rendered state with downstream pipelines', () => { - beforeEach(() => { - createComponent({ - downstreamPipelines: mockLinkedPipelines.triggered, - pipelinePath: 'my/pipeline/path', - }); - }); - - it('should have the correct props', () => { - expect(findPipelineMiniGraph().props()).toMatchObject({ - downstreamPipelines: expect.any(Array), - isMergeTrain: false, - pipelinePath: 'my/pipeline/path', - stages: expect.any(Array), - stagesClass: '', - updateDropdown: false, - upstreamPipeline: undefined, - }); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('should render the downstream linked pipelines mini list only', () => { - expect(findLinkedPipelineDownstream().exists()).toBe(true); - expect(findLinkedPipelineUpstream().exists()).toBe(false); - }); - - it('should render a downstream arrow icon only', () => { - expect(findUpstreamArrowIcon().exists()).toBe(false); - expect(findDownstreamArrowIcon().exists()).toBe(true); - expect(findDownstreamArrowIcon().props('name')).toBe('long-arrow'); - }); - }); -}); diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index 07818b9dadbe9129ca667de3ecfbb9c7afecac53..7b49baa5a204586f10ed97b826ca7f9e4159782a 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -113,28 +113,40 @@ describe('Pipelines Table', () => { }); describe('stages cell', () => { - it('should render pipeline mini graph', () => { + it('should render a pipeline mini graph', () => { expect(findPipelineMiniGraph().exists()).toBe(true); }); it('should render the right number of stages', () => { const stagesLength = pipeline.details.stages.length; - expect(findPipelineMiniGraph().props('stages').length).toBe(stagesLength); + expect( + findPipelineMiniGraph().findAll('[data-testid="mini-pipeline-graph-dropdown"]'), + ).toHaveLength(stagesLength); }); describe('when pipeline does not have stages', () => { beforeEach(() => { pipeline = createMockPipeline(); - pipeline.details.stages = []; + pipeline.details.stages = null; createComponent({ pipelines: [pipeline] }); }); it('stages are not rendered', () => { - expect(findPipelineMiniGraph().props('stages')).toHaveLength(0); + expect(findPipelineMiniGraph().exists()).toBe(false); }); }); + it('should not update dropdown', () => { + expect(findPipelineMiniGraph().props('updateDropdown')).toBe(false); + }); + + it('when update graph dropdown is set, should update graph dropdown', () => { + createComponent({ pipelines: [pipeline], updateGraphDropdown: true }); + + expect(findPipelineMiniGraph().props('updateDropdown')).toBe(true); + }); + it('when action request is complete, should refresh table', () => { findPipelineMiniGraph().vm.$emit('pipelineActionRequestComplete'); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js index a32f61c4567bf6cd2bb7a18627afde00615fbd83..6347e3c3be34cc706c3001069a2b1a0a87c2580d 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -4,8 +4,9 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { trimText } from 'helpers/text_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import MRWidgetPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; +import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; +import PipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; import { SUCCESS } from '~/vue_merge_request_widget/constants'; import mockData from '../mock_data'; @@ -29,13 +30,14 @@ describe('MRWidgetPipeline', () => { const findPipelineInfoContainer = () => wrapper.findByTestId('pipeline-info-container'); const findCommitLink = () => wrapper.findByTestId('commit-link'); const findPipelineFinishedAt = () => wrapper.findByTestId('finished-at'); + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findAllPipelineStages = () => wrapper.findAllComponents(PipelineStage); const findPipelineCoverage = () => wrapper.findByTestId('pipeline-coverage'); const findPipelineCoverageDelta = () => wrapper.findByTestId('pipeline-coverage-delta'); const findPipelineCoverageTooltipText = () => wrapper.findByTestId('pipeline-coverage-tooltip').text(); const findPipelineCoverageDeltaTooltipText = () => wrapper.findByTestId('pipeline-coverage-delta-tooltip').text(); - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); const findMonitoringPipelineMessage = () => wrapper.findByTestId('monitoring-pipeline-message'); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); @@ -43,7 +45,7 @@ describe('MRWidgetPipeline', () => { const createWrapper = (props = {}, mountFn = shallowMount) => { wrapper = extendedWrapper( - mountFn(MRWidgetPipelineComponent, { + mountFn(PipelineComponent, { propsData: { ...defaultProps, ...props, @@ -104,10 +106,8 @@ describe('MRWidgetPipeline', () => { }); it('should render pipeline graph', () => { - const stagesCount = mockData.pipeline.details.stages.length; - expect(findPipelineMiniGraph().exists()).toBe(true); - expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount); + expect(findAllPipelineStages()).toHaveLength(mockData.pipeline.details.stages.length); }); describe('should render pipeline coverage information', () => { @@ -176,11 +176,15 @@ describe('MRWidgetPipeline', () => { expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label); }); - it('should render pipeline graph', () => { + it('should render pipeline graph with correct styles', () => { const stagesCount = mockData.pipeline.details.stages.length; expect(findPipelineMiniGraph().exists()).toBe(true); - expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount); + expect(findPipelineMiniGraph().findAll('.mr-widget-pipeline-stages')).toHaveLength( + stagesCount, + ); + + expect(findAllPipelineStages()).toHaveLength(stagesCount); }); it('should render coverage information', () => {