diff options
Diffstat (limited to 'spec/frontend/commit')
-rw-r--r-- | spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js | 216 | ||||
-rw-r--r-- | spec/frontend/commit/commit_pipeline_status_component_spec.js | 4 | ||||
-rw-r--r-- | spec/frontend/commit/mock_data.js | 211 | ||||
-rw-r--r-- | spec/frontend/commit/pipelines/pipelines_table_spec.js | 27 |
4 files changed, 332 insertions, 126 deletions
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 b1c8ba48475..fddc767953a 100644 --- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js +++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js @@ -1,14 +1,24 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import { GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import createMockApollo from 'helpers/mock_apollo_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 PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/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'; -import { mockPipelineStagesQueryResponse, mockStages } from './mock_data'; +import * as graphQlUtils from '~/pipelines/components/graph/utils'; +import { + mockDownstreamQueryResponse, + mockPipelineStagesQueryResponse, + mockStages, + mockUpstreamDownstreamQueryResponse, + mockUpstreamQueryResponse, +} from './mock_data'; jest.mock('~/flash'); @@ -17,61 +27,219 @@ Vue.use(VueApollo); describe('Commit box pipeline mini graph', () => { let wrapper; - 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 findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const downstreamHandler = jest.fn().mockResolvedValue(mockDownstreamQueryResponse); + const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse); + const upstreamDownstreamHandler = jest + .fn() + .mockResolvedValue(mockUpstreamDownstreamQueryResponse); + const upstreamHandler = jest.fn().mockResolvedValue(mockUpstreamQueryResponse); + const advanceToNextFetch = () => { + jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL); + }; - const createComponent = ({ props = {} } = {}) => { - const handlers = [ - [getLinkedPipelinesQuery, {}], + const fullPath = 'gitlab-org/gitlab'; + const iid = '315'; + const createMockApolloProvider = (handler = downstreamHandler) => { + const requestHandlers = [ + [getLinkedPipelinesQuery, handler], [getPipelineStagesQuery, stagesHandler], ]; + return createMockApollo(requestHandlers); + }; + + const createComponent = (handler) => { wrapper = extendedWrapper( shallowMount(CommitBoxPipelineMiniGraph, { propsData: { stages: mockStages, - ...props, }, - apolloProvider: createMockApollo(handlers), + provide: { + fullPath, + iid, + dataMethod: 'graphql', + graphqlResourceEtag: '/api/graphql:pipelines/id/320', + }, + apolloProvider: createMockApolloProvider(handler), }), ); - - return waitForPromises(); }; afterEach(() => { wrapper.destroy(); }); - describe('linked pipelines', () => { + describe('loading state', () => { + it('should display loading state when loading', () => { + createComponent(); + + expect(findLoadingIcon().exists()).toBe(true); + expect(findPipelineMiniGraph().exists()).toBe(false); + }); + }); + + describe('loaded state', () => { beforeEach(async () => { await createComponent(); }); - it('should display the mini pipeine graph', () => { - expect(findMiniGraph().exists()).toBe(true); + it('should not display loading state after the query is resolved', async () => { + expect(findLoadingIcon().exists()).toBe(false); + expect(findPipelineMiniGraph().exists()).toBe(true); }); - it('should not display linked pipelines', () => { - expect(findUpstream().exists()).toBe(false); - expect(findDownstream().exists()).toBe(false); + it('should display the pipeline mini graph', () => { + expect(findPipelineMiniGraph().exists()).toBe(true); }); }); - describe('when data is mismatched', () => { - beforeEach(async () => { - await createComponent({ props: { stages: [] } }); + describe('load upstream/downstream', () => { + const samplePipeline = { + __typename: expect.any(String), + id: expect.any(String), + path: expect.any(String), + project: expect.any(Object), + detailedStatus: expect.any(Object), + }; + + it('formatted stages should be passed to the pipeline mini graph', async () => { + const stage = mockStages[0]; + const expectedStages = [ + { + name: stage.name, + status: { + __typename: 'DetailedStatus', + id: stage.status.id, + icon: stage.status.icon, + group: stage.status.group, + }, + dropdown_path: stage.dropdown_path, + title: stage.title, + }, + ]; + + createComponent(); + + await waitForPromises(); + + expect(findPipelineMiniGraph().props('stages')).toEqual(expectedStages); + }); + + 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 pass the pipeline path prop for the counter badge', async () => { + createComponent(downstreamHandler); + + await waitForPromises(); + + const expectedPath = mockDownstreamQueryResponse.data.project.pipeline.path; + const pipelinePath = findPipelineMiniGraph().props('pipelinePath'); + + expect(pipelinePath).toBe(expectedPath); + }); + + 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); }); - it('calls create flash with expected arguments', () => { + it('should render downstream and upstream pipelines', async () => { + createComponent(upstreamDownstreamHandler); + + await waitForPromises(); + + const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); + const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); + + expect(upstreamPipeline).toEqual(samplePipeline); + expect(downstreamPipelines).toEqual(expect.arrayContaining([samplePipeline])); + }); + }); + + describe('error state', () => { + it('createFlash should show if there is an error fetching the data', async () => { + createComponent({ handler: failedHandler }); + + await waitForPromises(); + 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'), + message: 'There was a problem fetching linked pipelines.', }); }); }); + + describe('polling', () => { + it('polling interval is set for linked pipelines', () => { + createComponent(); + + const expectedInterval = wrapper.vm.$apollo.queries.pipeline.options.pollInterval; + + expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL); + }); + + it('polling interval is set for pipeline stages', () => { + createComponent(); + + const expectedInterval = wrapper.vm.$apollo.queries.pipelineStages.options.pollInterval; + + expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL); + }); + + it('polls for stages and linked pipelines', async () => { + createComponent(); + + await waitForPromises(); + + expect(stagesHandler).toHaveBeenCalledTimes(1); + expect(downstreamHandler).toHaveBeenCalledTimes(1); + + advanceToNextFetch(); + await waitForPromises(); + + expect(stagesHandler).toHaveBeenCalledTimes(2); + expect(downstreamHandler).toHaveBeenCalledTimes(2); + + advanceToNextFetch(); + await waitForPromises(); + + expect(stagesHandler).toHaveBeenCalledTimes(3); + expect(downstreamHandler).toHaveBeenCalledTimes(3); + }); + + it('toggles query polling with visibility check', async () => { + jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility'); + + createComponent(); + + await waitForPromises(); + + expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith( + wrapper.vm.$apollo.queries.pipelineStages, + ); + expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith( + wrapper.vm.$apollo.queries.pipeline, + ); + }); + }); }); diff --git a/spec/frontend/commit/commit_pipeline_status_component_spec.js b/spec/frontend/commit/commit_pipeline_status_component_spec.js index 43db6db00c1..73720c1cc88 100644 --- a/spec/frontend/commit/commit_pipeline_status_component_spec.js +++ b/spec/frontend/commit/commit_pipeline_status_component_spec.js @@ -37,9 +37,9 @@ describe('Commit pipeline status component', () => { }); }; - const findLoader = () => wrapper.find(GlLoadingIcon); + const findLoader = () => wrapper.findComponent(GlLoadingIcon); const findLink = () => wrapper.find('a'); - const findCiIcon = () => findLink().find(CiIcon); + const findCiIcon = () => findLink().findComponent(CiIcon); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/commit/mock_data.js b/spec/frontend/commit/mock_data.js index 8db162c07c2..aef137e6fa5 100644 --- a/spec/frontend/commit/mock_data.js +++ b/spec/frontend/commit/mock_data.js @@ -3,116 +3,21 @@ export const mockStages = [ name: 'build', title: 'build: passed', status: { + __typename: 'DetailedStatus', + id: 'success-409-409', icon: 'status_success', text: 'passed', label: 'passed', group: 'success', tooltip: 'passed', has_details: true, - details_path: '/root/ci-project/-/pipelines/611#build', + details_path: '/root/ci-project/-/pipelines/318#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', - }, - { - name: 'test', - title: 'test: passed', - status: { - icon: 'status_success', - text: 'passed', - label: 'passed', - 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', - }, - { - name: 'test_two', - title: 'test_two: passed', - status: { - icon: 'status_success', - text: 'passed', - 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', - }, - { - name: 'manual', - title: 'manual: skipped', - status: { - 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: { - 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', - }, - { - 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/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', - }, - { - name: 'qa', - title: 'qa: passed', - status: { - icon: 'status_success', - text: 'passed', - label: 'passed', - group: 'success', - tooltip: 'passed', - has_details: true, - details_path: '/root/ci-project/-/pipelines/611#qa', - illustration: null, - favicon: - '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', - }, - path: '/root/ci-project/-/pipelines/611#qa', - dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa', + path: '/root/ci-project/-/pipelines/318#build', + dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=build', }, ]; @@ -161,3 +66,109 @@ export const mockPipelineStatusResponse = { }, }, }; + +export const mockDownstreamQueryResponse = { + data: { + project: { + id: '1', + pipeline: { + path: '/root/ci-project/-/pipelines/790', + id: 'pipeline-1', + downstream: { + nodes: [ + { + id: 'gid://gitlab/Ci::Pipeline/612', + path: '/root/job-log-sections/-/pipelines/612', + project: { id: '1', name: 'job-log-sections', __typename: 'Project' }, + detailedStatus: { + id: 'status-1', + group: 'success', + icon: 'status_success', + label: 'passed', + __typename: 'DetailedStatus', + }, + __typename: 'Pipeline', + }, + ], + __typename: 'PipelineConnection', + }, + upstream: null, + }, + __typename: 'Project', + }, + }, +}; + +export const mockUpstreamDownstreamQueryResponse = { + data: { + project: { + id: '1', + pipeline: { + id: 'pipeline-1', + path: '/root/ci-project/-/pipelines/790', + downstream: { + nodes: [ + { + id: 'gid://gitlab/Ci::Pipeline/612', + path: '/root/job-log-sections/-/pipelines/612', + project: { id: '1', name: 'job-log-sections', __typename: 'Project' }, + detailedStatus: { + id: 'status-1', + group: 'success', + icon: 'status_success', + label: 'passed', + __typename: 'DetailedStatus', + }, + __typename: 'Pipeline', + }, + ], + __typename: 'PipelineConnection', + }, + upstream: { + id: 'gid://gitlab/Ci::Pipeline/610', + path: '/root/trigger-downstream/-/pipelines/610', + project: { id: '1', name: 'trigger-downstream', __typename: 'Project' }, + detailedStatus: { + id: 'status-1', + group: 'success', + icon: 'status_success', + label: 'passed', + __typename: 'DetailedStatus', + }, + __typename: 'Pipeline', + }, + }, + __typename: 'Project', + }, + }, +}; + +export const mockUpstreamQueryResponse = { + data: { + project: { + id: '1', + pipeline: { + id: 'pipeline-1', + path: '/root/ci-project/-/pipelines/790', + downstream: { + nodes: [], + __typename: 'PipelineConnection', + }, + upstream: { + id: 'gid://gitlab/Ci::Pipeline/610', + path: '/root/trigger-downstream/-/pipelines/610', + project: { id: '1', name: 'trigger-downstream', __typename: 'Project' }, + detailedStatus: { + id: 'status-1', + group: 'success', + icon: 'status_success', + label: 'passed', + __typename: 'DetailedStatus', + }, + __typename: 'Pipeline', + }, + }, + __typename: 'Project', + }, + }, +}; diff --git a/spec/frontend/commit/pipelines/pipelines_table_spec.js b/spec/frontend/commit/pipelines/pipelines_table_spec.js index 71ee12cf02d..d89a238105b 100644 --- a/spec/frontend/commit/pipelines/pipelines_table_spec.js +++ b/spec/frontend/commit/pipelines/pipelines_table_spec.js @@ -302,6 +302,33 @@ describe('Pipelines table in Commits and Merge requests', () => { expect(findModal()).not.toBeNull(); }); }); + + describe('when no pipelines were created on a forked merge request', () => { + beforeEach(async () => { + mock.onGet('endpoint.json').reply(200, []); + + createComponent({ + projectId: '5', + mergeRequestId: 3, + canCreatePipelineInTargetProject: true, + sourceProjectFullPath: 'test/parent-project', + targetProjectFullPath: 'test/fork-project', + }); + + jest.spyOn(findModal().vm, 'show').mockReturnValue(); + + await waitForPromises(); + }); + + it('should show security modal from empty state run pipeline button', () => { + expect(findEmptyState().exists()).toBe(true); + expect(findModal().exists()).toBe(true); + + findRunPipelineBtn().trigger('click'); + + expect(findModal().vm.show).toHaveBeenCalled(); + }); + }); }); describe('unsuccessfull request', () => { |