diff options
Diffstat (limited to 'spec/frontend/commit')
-rw-r--r-- | spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js | 57 | ||||
-rw-r--r-- | spec/frontend/commit/components/commit_box_pipeline_status_spec.js | 150 | ||||
-rw-r--r-- | spec/frontend/commit/mock_data.js | 46 | ||||
-rw-r--r-- | spec/frontend/commit/pipelines/pipelines_table_spec.js | 16 | ||||
-rw-r--r-- | spec/frontend/commit/pipelines/utils_spec.js | 59 |
5 files changed, 306 insertions, 22 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 1a2e188e7ae..b1c8ba48475 100644 --- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js +++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js @@ -1,7 +1,18 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; 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 { 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', () => { let wrapper; @@ -10,34 +21,36 @@ describe('Commit box pipeline mini graph', () => { const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); 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( shallowMount(CommitBoxPipelineMiniGraph, { propsData: { stages: mockStages, + ...props, }, - mocks: { - $apollo: { - queries: { - pipeline: { - loading: false, - }, - }, - }, - }, + apolloProvider: createMockApollo(handlers), }), ); - }; - beforeEach(() => { - createComponent(); - }); + return waitForPromises(); + }; afterEach(() => { wrapper.destroy(); }); describe('linked pipelines', () => { + beforeEach(async () => { + await createComponent(); + }); + it('should display the mini pipeine graph', () => { expect(findMiniGraph().exists()).toBe(true); }); @@ -47,4 +60,18 @@ describe('Commit box pipeline mini graph', () => { 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'), + }); + }); + }); }); diff --git a/spec/frontend/commit/components/commit_box_pipeline_status_spec.js b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js new file mode 100644 index 00000000000..db7b7b45397 --- /dev/null +++ b/spec/frontend/commit/components/commit_box_pipeline_status_spec.js @@ -0,0 +1,150 @@ +import { GlLoadingIcon, GlLink } from '@gitlab/ui'; +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 createFlash from '~/flash'; +import CiIcon from '~/vue_shared/components/ci_icon.vue'; +import CommitBoxPipelineStatus from '~/projects/commit_box/info/components/commit_box_pipeline_status.vue'; +import { + COMMIT_BOX_POLL_INTERVAL, + PIPELINE_STATUS_FETCH_ERROR, +} from '~/projects/commit_box/info/constants'; +import getLatestPipelineStatusQuery from '~/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql'; +import * as graphQlUtils from '~/pipelines/components/graph/utils'; +import { mockPipelineStatusResponse } from '../mock_data'; + +const mockProvide = { + fullPath: 'root/ci-project', + iid: '46', + graphqlResourceEtag: '/api/graphql:pipelines/id/320', +}; + +Vue.use(VueApollo); + +jest.mock('~/flash'); + +describe('Commit box pipeline status', () => { + let wrapper; + + const statusSuccessHandler = jest.fn().mockResolvedValue(mockPipelineStatusResponse); + const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); + + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findStatusIcon = () => wrapper.findComponent(CiIcon); + const findPipelineLink = () => wrapper.findComponent(GlLink); + + const advanceToNextFetch = () => { + jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL); + }; + + const createMockApolloProvider = (handler) => { + const requestHandlers = [[getLatestPipelineStatusQuery, handler]]; + + return createMockApollo(requestHandlers); + }; + + const createComponent = (handler = statusSuccessHandler) => { + wrapper = shallowMount(CommitBoxPipelineStatus, { + provide: { + ...mockProvide, + }, + apolloProvider: createMockApolloProvider(handler), + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('loading state', () => { + it('should display loading state when loading', () => { + createComponent(); + + expect(findLoadingIcon().exists()).toBe(true); + expect(findStatusIcon().exists()).toBe(false); + }); + }); + + describe('loaded state', () => { + beforeEach(async () => { + createComponent(); + + await waitForPromises(); + }); + + it('should display pipeline status after the query is resolved successfully', async () => { + expect(findStatusIcon().exists()).toBe(true); + + expect(findLoadingIcon().exists()).toBe(false); + expect(createFlash).toHaveBeenCalledTimes(0); + }); + + it('should link to the latest pipeline', () => { + const { + data: { + project: { + pipeline: { + detailedStatus: { detailsPath }, + }, + }, + }, + } = mockPipelineStatusResponse; + + expect(findPipelineLink().attributes('href')).toBe(detailsPath); + }); + }); + + describe('error state', () => { + it('createFlash should show if there is an error fetching the pipeline status', async () => { + createComponent(failedHandler); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: PIPELINE_STATUS_FETCH_ERROR, + }); + }); + }); + + describe('polling', () => { + it('polling interval is set for pipeline stages', () => { + createComponent(); + + const expectedInterval = wrapper.vm.$apollo.queries.pipelineStatus.options.pollInterval; + + expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL); + }); + + it('polls for pipeline status', async () => { + createComponent(); + + await waitForPromises(); + + expect(statusSuccessHandler).toHaveBeenCalledTimes(1); + + advanceToNextFetch(); + await waitForPromises(); + + expect(statusSuccessHandler).toHaveBeenCalledTimes(2); + + advanceToNextFetch(); + await waitForPromises(); + + expect(statusSuccessHandler).toHaveBeenCalledTimes(3); + }); + + it('toggles pipelineStatus polling with visibility check', async () => { + jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility'); + + createComponent(); + + await waitForPromises(); + + expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith( + wrapper.vm.$apollo.queries.pipelineStatus, + ); + }); + }); +}); diff --git a/spec/frontend/commit/mock_data.js b/spec/frontend/commit/mock_data.js index ef018a4fbd7..8db162c07c2 100644 --- a/spec/frontend/commit/mock_data.js +++ b/spec/frontend/commit/mock_data.js @@ -115,3 +115,49 @@ export const mockStages = [ 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', + }, + }, + ], + }, + }, + }, + }, +}; + +export const mockPipelineStatusResponse = { + data: { + project: { + id: 'gid://gitlab/Project/20', + pipeline: { + id: 'gid://gitlab/Ci::Pipeline/320', + detailedStatus: { + id: 'pending-320-320', + detailsPath: '/root/ci-project/-/pipelines/320', + icon: 'status_pending', + group: 'pending', + __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 203a4d23160..9b01af1e585 100644 --- a/spec/frontend/commit/pipelines/pipelines_table_spec.js +++ b/spec/frontend/commit/pipelines/pipelines_table_spec.js @@ -120,18 +120,20 @@ describe('Pipelines table in Commits and Merge requests', () => { }); describe('pipeline badge counts', () => { - it('should receive update-pipelines-count event', (done) => { + it('should receive update-pipelines-count event', () => { const element = document.createElement('div'); document.body.appendChild(element); - element.addEventListener('update-pipelines-count', (event) => { - expect(event.detail.pipelineCount).toEqual(10); - done(); - }); + return new Promise((resolve) => { + element.addEventListener('update-pipelines-count', (event) => { + expect(event.detail.pipelineCount).toEqual(10); + resolve(); + }); - createComponent(); + createComponent(); - element.appendChild(wrapper.vm.$el); + element.appendChild(wrapper.vm.$el); + }); }); }); }); diff --git a/spec/frontend/commit/pipelines/utils_spec.js b/spec/frontend/commit/pipelines/utils_spec.js new file mode 100644 index 00000000000..472e35a6eb3 --- /dev/null +++ b/spec/frontend/commit/pipelines/utils_spec.js @@ -0,0 +1,59 @@ +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'); + }); +}); |