diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /spec/frontend/pipelines | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) | |
download | gitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'spec/frontend/pipelines')
9 files changed, 199 insertions, 42 deletions
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js index 062c9759a65..5a17be1af23 100644 --- a/spec/frontend/pipelines/graph/graph_component_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_spec.js @@ -159,13 +159,13 @@ describe('graph component', () => { describe('triggered by', () => { describe('on click', () => { - it('should emit `onClickTriggeredBy` when triggered by linked pipeline is clicked', () => { + it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => { const btnWrapper = findExpandPipelineBtn(); btnWrapper.trigger('click'); btnWrapper.vm.$nextTick(() => { - expect(wrapper.emitted().onClickTriggeredBy).toEqual([ + expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([ store.state.pipeline.triggered_by, ]); }); diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js index 8e65f0d4f71..67986ca7739 100644 --- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js @@ -2,11 +2,10 @@ import { mount } from '@vue/test-utils'; import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue'; import CiStatus from '~/vue_shared/components/ci_icon.vue'; - import mockData from './linked_pipelines_mock_data'; +import { UPSTREAM, DOWNSTREAM } from '~/pipelines/components/graph/constants'; const mockPipeline = mockData.triggered[0]; - const validTriggeredPipelineId = mockPipeline.project.id; const invalidTriggeredPipelineId = mockPipeline.project.id + 5; @@ -40,6 +39,7 @@ describe('Linked pipeline', () => { pipeline: mockPipeline, projectId: invalidTriggeredPipelineId, columnTitle: 'Downstream', + type: DOWNSTREAM, }; beforeEach(() => { @@ -104,11 +104,13 @@ describe('Linked pipeline', () => { pipeline: mockPipeline, projectId: validTriggeredPipelineId, columnTitle: 'Downstream', + type: DOWNSTREAM, }; const upstreamProps = { ...downstreamProps, columnTitle: 'Upstream', + type: UPSTREAM, }; it('parent/child label container should exist', () => { @@ -182,6 +184,7 @@ describe('Linked pipeline', () => { pipeline: { ...mockPipeline, isLoading: true }, projectId: invalidTriggeredPipelineId, columnTitle: 'Downstream', + type: DOWNSTREAM, }; beforeEach(() => { @@ -198,6 +201,7 @@ describe('Linked pipeline', () => { pipeline: mockPipeline, projectId: validTriggeredPipelineId, columnTitle: 'Downstream', + type: DOWNSTREAM, }; beforeEach(() => { diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js index 82eaa553d0c..e6ae3154d1d 100644 --- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue'; import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue'; +import { UPSTREAM } from '~/pipelines/components/graph/constants'; import mockData from './linked_pipelines_mock_data'; describe('Linked Pipelines Column', () => { @@ -9,6 +10,7 @@ describe('Linked Pipelines Column', () => { linkedPipelines: mockData.triggered, graphPosition: 'right', projectId: 19, + type: UPSTREAM, }; let wrapper; diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js index 2e10b0f068c..03e385e3cc8 100644 --- a/spec/frontend/pipelines/header_component_spec.js +++ b/spec/frontend/pipelines/header_component_spec.js @@ -1,19 +1,19 @@ import { shallowMount } from '@vue/test-utils'; import { GlModal, GlLoadingIcon } from '@gitlab/ui'; -import MockAdapter from 'axios-mock-adapter'; import { mockCancelledPipelineHeader, mockFailedPipelineHeader, mockRunningPipelineHeader, mockSuccessfulPipelineHeader, } from './mock_data'; -import axios from '~/lib/utils/axios_utils'; import HeaderComponent from '~/pipelines/components/header_component.vue'; +import deletePipelineMutation from '~/pipelines/graphql/mutations/delete_pipeline.mutation.graphql'; +import retryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql'; +import cancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql'; describe('Pipeline details header', () => { let wrapper; let glModalDirective; - let mockAxios; const findDeleteModal = () => wrapper.find(GlModal); const findRetryButton = () => wrapper.find('[data-testid="retryPipeline"]'); @@ -25,9 +25,7 @@ describe('Pipeline details header', () => { pipelineId: 14, pipelineIid: 1, paths: { - retry: '/retry', - cancel: '/cancel', - delete: '/delete', + pipelinesPath: '/namespace/my-project/-/pipelines', fullProject: '/namespace/my-project', }, }; @@ -43,6 +41,7 @@ describe('Pipeline details header', () => { startPolling: jest.fn(), }, }, + mutate: jest.fn(), }; return shallowMount(HeaderComponent, { @@ -65,16 +64,9 @@ describe('Pipeline details header', () => { }); }; - beforeEach(() => { - mockAxios = new MockAdapter(axios); - mockAxios.onGet('*').replyOnce(200); - }); - afterEach(() => { wrapper.destroy(); wrapper = null; - - mockAxios.restore(); }); describe('initial loading', () => { @@ -105,19 +97,37 @@ describe('Pipeline details header', () => { ); }); + describe('polling', () => { + it('is stopped when pipeline is finished', async () => { + wrapper = createComponent({ ...mockRunningPipelineHeader }); + + await wrapper.setData({ + pipeline: { ...mockCancelledPipelineHeader }, + }); + + expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).toHaveBeenCalled(); + }); + + it('is not stopped when pipeline is not finished', () => { + wrapper = createComponent(); + + expect(wrapper.vm.$apollo.queries.pipeline.stopPolling).not.toHaveBeenCalled(); + }); + }); + describe('actions', () => { describe('Retry action', () => { beforeEach(() => { wrapper = createComponent(mockCancelledPipelineHeader); }); - it('should call axios with the right path when retry button is clicked', async () => { - jest.spyOn(axios, 'post'); + it('should call retryPipeline Mutation with pipeline id', () => { findRetryButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); - - expect(axios.post).toHaveBeenCalledWith(defaultProvideOptions.paths.retry); + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: retryPipelineMutation, + variables: { id: mockCancelledPipelineHeader.id }, + }); }); }); @@ -126,13 +136,13 @@ describe('Pipeline details header', () => { wrapper = createComponent(mockRunningPipelineHeader); }); - it('should call axios with the right path when cancel button is clicked', async () => { - jest.spyOn(axios, 'post'); + it('should call cancelPipeline Mutation with pipeline id', () => { findCancelButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); - - expect(axios.post).toHaveBeenCalledWith(defaultProvideOptions.paths.cancel); + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: cancelPipelineMutation, + variables: { id: mockRunningPipelineHeader.id }, + }); }); }); @@ -141,24 +151,21 @@ describe('Pipeline details header', () => { wrapper = createComponent(mockFailedPipelineHeader); }); - it('displays delete modal when clicking on delete and does not call the delete action', async () => { - jest.spyOn(axios, 'delete'); + it('displays delete modal when clicking on delete and does not call the delete action', () => { findDeleteButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); - expect(findDeleteModal().props('modalId')).toBe(wrapper.vm.$options.DELETE_MODAL_ID); expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.$options.DELETE_MODAL_ID); - expect(axios.delete).not.toHaveBeenCalled(); + expect(wrapper.vm.$apollo.mutate).not.toHaveBeenCalled(); }); - it('should call delete path when modal is submitted', async () => { - jest.spyOn(axios, 'delete'); + it('should call deletePipeline Mutation with pipeline id when modal is submitted', () => { findDeleteModal().vm.$emit('ok'); - await wrapper.vm.$nextTick(); - - expect(axios.delete).toHaveBeenCalledWith(defaultProvideOptions.paths.delete); + expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ + mutation: deletePipelineMutation, + variables: { id: mockFailedPipelineHeader.id }, + }); }); }); }); diff --git a/spec/frontend/pipelines/pipeline_graph/mock_data.js b/spec/frontend/pipelines/pipeline_graph/mock_data.js index b50932deec6..4f55fdd6b28 100644 --- a/spec/frontend/pipelines/pipeline_graph/mock_data.js +++ b/spec/frontend/pipelines/pipeline_graph/mock_data.js @@ -91,3 +91,18 @@ export const pipelineData = { [jobId4]: {}, }, }; + +export const singleStageData = { + stages: [ + { + name: 'build', + groups: [ + { + name: 'build_1', + jobs: [{ script: 'echo hello', stage: 'build' }], + id: jobId1, + }, + ], + }, + ], +}; diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js index 30e192e5726..7c8ebc27974 100644 --- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js +++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import { pipelineData } from './mock_data'; +import { pipelineData, singleStageData } from './mock_data'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import StagePill from '~/pipelines/components/pipeline_graph/stage_pill.vue'; import JobPill from '~/pipelines/components/pipeline_graph/job_pill.vue'; @@ -18,6 +18,8 @@ describe('pipeline graph component', () => { }; const findAllStagePills = () => wrapper.findAll(StagePill); + const findAllStageBackgroundElements = () => wrapper.findAll('[data-testid="stage-background"]'); + const findStageBackgroundElementAt = index => findAllStageBackgroundElements().at(index); const findAllJobPills = () => wrapper.findAll(JobPill); afterEach(() => { @@ -31,7 +33,9 @@ describe('pipeline graph component', () => { }); it('renders an empty section', () => { - expect(wrapper.text()).toContain('No content to show'); + expect(wrapper.text()).toContain( + 'The visualization will appear in this tab when the CI/CD configuration file is populated with valid syntax.', + ); expect(findAllStagePills()).toHaveLength(0); expect(findAllJobPills()).toHaveLength(0); }); @@ -41,12 +45,43 @@ describe('pipeline graph component', () => { beforeEach(() => { wrapper = createComponent(); }); + it('renders the right number of stage pills', () => { const expectedStagesLength = pipelineData.stages.length; expect(findAllStagePills()).toHaveLength(expectedStagesLength); }); + it.each` + cssClass | expectedState + ${'gl-rounded-bottom-left-6'} | ${true} + ${'gl-rounded-top-left-6'} | ${true} + ${'gl-rounded-top-right-6'} | ${false} + ${'gl-rounded-bottom-right-6'} | ${false} + `( + 'rounds corner: $class should be $expectedState on the first element', + ({ cssClass, expectedState }) => { + const classes = findStageBackgroundElementAt(0).classes(); + + expect(classes.includes(cssClass)).toBe(expectedState); + }, + ); + + it.each` + cssClass | expectedState + ${'gl-rounded-bottom-left-6'} | ${false} + ${'gl-rounded-top-left-6'} | ${false} + ${'gl-rounded-top-right-6'} | ${true} + ${'gl-rounded-bottom-right-6'} | ${true} + `( + 'rounds corner: $class should be $expectedState on the last element', + ({ cssClass, expectedState }) => { + const classes = findStageBackgroundElementAt(pipelineData.stages.length - 1).classes(); + + expect(classes.includes(cssClass)).toBe(expectedState); + }, + ); + it('renders the right number of job pills', () => { // We count the number of jobs in the mock data const expectedJobsLength = pipelineData.stages.reduce((acc, val) => { @@ -56,4 +91,25 @@ describe('pipeline graph component', () => { expect(findAllJobPills()).toHaveLength(expectedJobsLength); }); }); + + describe('with only one stage', () => { + beforeEach(() => { + wrapper = createComponent({ pipelineData: singleStageData }); + }); + + it.each` + cssClass | expectedState + ${'gl-rounded-bottom-left-6'} | ${true} + ${'gl-rounded-top-left-6'} | ${true} + ${'gl-rounded-top-right-6'} | ${true} + ${'gl-rounded-bottom-right-6'} | ${true} + `( + 'rounds corner: $class should be $expectedState on the only element', + ({ cssClass, expectedState }) => { + const classes = findStageBackgroundElementAt(0).classes(); + + expect(classes.includes(cssClass)).toBe(expectedState); + }, + ); + }); }); diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js index 1298a2a1524..a272803f9b6 100644 --- a/spec/frontend/pipelines/pipelines_spec.js +++ b/spec/frontend/pipelines/pipelines_spec.js @@ -74,7 +74,6 @@ describe('Pipelines', () => { const createComponent = (props = defaultProps, methods) => { wrapper = mount(PipelinesComponent, { - provide: { glFeatures: { filterPipelinesSearch: true } }, propsData: { store: new Store(), projectId: '21', @@ -373,7 +372,6 @@ describe('Pipelines', () => { }); it('should render table', () => { - expect(wrapper.find('.table-holder').exists()).toBe(true); expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength( pipelines.pipelines.length + 1, ); diff --git a/spec/frontend/pipelines/test_reports/test_case_details_spec.js b/spec/frontend/pipelines/test_reports/test_case_details_spec.js new file mode 100644 index 00000000000..9e66012818e --- /dev/null +++ b/spec/frontend/pipelines/test_reports/test_case_details_spec.js @@ -0,0 +1,74 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlModal } from '@gitlab/ui'; +import TestCaseDetails from '~/pipelines/components/test_reports/test_case_details.vue'; +import CodeBlock from '~/vue_shared/components/code_block.vue'; + +const localVue = createLocalVue(); + +describe('Test case details', () => { + let wrapper; + const defaultTestCase = { + classname: 'spec.test_spec', + name: 'Test#something cool', + formattedTime: '10.04ms', + system_output: 'Line 42 is broken', + }; + + const findModal = () => wrapper.find(GlModal); + const findName = () => wrapper.find('[data-testid="test-case-name"]'); + const findDuration = () => wrapper.find('[data-testid="test-case-duration"]'); + const findSystemOutput = () => wrapper.find('[data-testid="test-case-trace"]'); + + const createComponent = (testCase = {}) => { + wrapper = shallowMount(TestCaseDetails, { + localVue, + propsData: { + modalId: 'my-modal', + testCase: { + ...defaultTestCase, + ...testCase, + }, + }, + stubs: { CodeBlock, GlModal }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('required details', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the test case classname as modal title', () => { + expect(findModal().attributes('title')).toBe(defaultTestCase.classname); + }); + + it('renders the test case name', () => { + expect(findName().text()).toBe(defaultTestCase.name); + }); + + it('renders the test case duration', () => { + expect(findDuration().text()).toBe(defaultTestCase.formattedTime); + }); + }); + + describe('when test case has system output', () => { + it('renders the test case system output', () => { + createComponent(); + + expect(findSystemOutput().text()).toContain(defaultTestCase.system_output); + }); + }); + + describe('when test case does not have system output', () => { + it('does not render the test case system output', () => { + createComponent({ system_output: null }); + + expect(findSystemOutput().exists()).toBe(false); + }); + }); +}); diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js index 838e0606375..284099b000b 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -1,7 +1,7 @@ import Vuex from 'vuex'; import { shallowMount, createLocalVue } from '@vue/test-utils'; import { getJSONFixture } from 'helpers/fixtures'; -import { GlButton } from '@gitlab/ui'; +import { GlButton, GlFriendlyWrap } from '@gitlab/ui'; import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue'; import * as getters from '~/pipelines/stores/test_reports/getters'; import { TestStatus } from '~/pipelines/constants'; @@ -40,6 +40,7 @@ describe('Test reports suite table', () => { wrapper = shallowMount(SuiteTable, { store, localVue, + stubs: { GlFriendlyWrap }, }); }; |