diff options
author | Robert Speicher <rspeicher@gmail.com> | 2021-01-20 13:34:23 -0600 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2021-01-20 13:34:23 -0600 |
commit | 6438df3a1e0fb944485cebf07976160184697d72 (patch) | |
tree | 00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /spec/frontend/pipelines | |
parent | 42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff) | |
download | gitlab-ce-6438df3a1e0fb944485cebf07976160184697d72.tar.gz |
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'spec/frontend/pipelines')
37 files changed, 573 insertions, 361 deletions
diff --git a/spec/frontend/pipelines/blank_state_spec.js b/spec/frontend/pipelines/blank_state_spec.js index bb069fdc2c8..c09d9232569 100644 --- a/spec/frontend/pipelines/blank_state_spec.js +++ b/spec/frontend/pipelines/blank_state_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; +import mountComponent from 'helpers/vue_mount_component_helper'; import component from '~/pipelines/components/pipelines_list/blank_state.vue'; -import mountComponent from '../helpers/vue_mount_component_helper'; describe('Pipelines Blank State', () => { let vm; diff --git a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js index 5747c91bee8..80807c0b330 100644 --- a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js @@ -66,11 +66,7 @@ describe('The DAG annotations', () => { expect(getAllTextBlocks().length).toBe(Object.keys(multiNote).length); Object.values(multiNote).forEach((item, idx) => { - expect( - getAllTextBlocks() - .at(idx) - .text(), - ).toBe(`${item.source.name} → ${item.target.name}`); + expect(getAllTextBlocks().at(idx).text()).toBe(`${item.source.name} → ${item.target.name}`); }); }); diff --git a/spec/frontend/pipelines/components/dag/dag_graph_spec.js b/spec/frontend/pipelines/components/dag/dag_graph_spec.js index 7786212cb69..ccfb2ae7cee 100644 --- a/spec/frontend/pipelines/components/dag/dag_graph_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_graph_spec.js @@ -20,7 +20,7 @@ describe('The DAG graph', () => { } wrapper = shallowMount(DagGraph, { - attachToDocument: true, + attachTo: document.body, propsData, data() { return { @@ -88,17 +88,13 @@ describe('The DAG graph', () => { }); it('renders the title as text', () => { - expect( - getAllLabels() - .at(0) - .text(), - ).toBe(parsedData.nodes[0].name); + expect(getAllLabels().at(0).text()).toBe(parsedData.nodes[0].name); }); }); }); describe('interactions', () => { - const strokeOpacity = opacity => `stroke-opacity: ${opacity};`; + const strokeOpacity = (opacity) => `stroke-opacity: ${opacity};`; const baseOpacity = () => wrapper.vm.$options.viewOptions.baseOpacity; describe('links', () => { @@ -168,10 +164,10 @@ describe('The DAG graph', () => { describe('nodes', () => { const liveNode = () => getAllNodes().at(10); const anotherLiveNode = () => getAllNodes().at(5); - const nodesNotHighlighted = () => getAllNodes().filter(n => !n.classes(IS_HIGHLIGHTED)); - const linksNotHighlighted = () => getAllLinks().filter(n => !n.classes(IS_HIGHLIGHTED)); - const nodesHighlighted = () => getAllNodes().filter(n => n.classes(IS_HIGHLIGHTED)); - const linksHighlighted = () => getAllLinks().filter(n => n.classes(IS_HIGHLIGHTED)); + const nodesNotHighlighted = () => getAllNodes().filter((n) => !n.classes(IS_HIGHLIGHTED)); + const linksNotHighlighted = () => getAllLinks().filter((n) => !n.classes(IS_HIGHLIGHTED)); + const nodesHighlighted = () => getAllNodes().filter((n) => n.classes(IS_HIGHLIGHTED)); + const linksHighlighted = () => getAllLinks().filter((n) => n.classes(IS_HIGHLIGHTED)); describe('on click', () => { it('highlights the clicked node and predecessors', () => { @@ -180,19 +176,19 @@ describe('The DAG graph', () => { expect(nodesNotHighlighted().length < getAllNodes().length).toBe(true); expect(linksNotHighlighted().length < getAllLinks().length).toBe(true); - linksHighlighted().wrappers.forEach(link => { + linksHighlighted().wrappers.forEach((link) => { expect(link.attributes('style')).toBe(strokeOpacity(highlightIn)); }); - nodesHighlighted().wrappers.forEach(node => { + nodesHighlighted().wrappers.forEach((node) => { expect(node.attributes('stroke')).not.toBe('#f2f2f2'); }); - linksNotHighlighted().wrappers.forEach(link => { + linksNotHighlighted().wrappers.forEach((link) => { expect(link.attributes('style')).toBe(strokeOpacity(highlightOut)); }); - nodesNotHighlighted().wrappers.forEach(node => { + nodesNotHighlighted().wrappers.forEach((node) => { expect(node.attributes('stroke')).toBe('#f2f2f2'); }); }); diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js index 08a43199594..f6195e30e44 100644 --- a/spec/frontend/pipelines/components/dag/dag_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_spec.js @@ -21,7 +21,7 @@ describe('Pipeline DAG graph wrapper', () => { const getAllAlerts = () => wrapper.findAll(GlAlert); const getGraph = () => wrapper.find(DagGraph); const getNotes = () => wrapper.find(DagAnnotations); - const getErrorText = type => wrapper.vm.$options.errorTexts[type]; + const getErrorText = (type) => wrapper.vm.$options.errorTexts[type]; const getEmptyState = () => wrapper.find(GlEmptyState); const createComponent = ({ diff --git a/spec/frontend/pipelines/components/dag/parsing_utils_spec.js b/spec/frontend/pipelines/components/dag/parsing_utils_spec.js index ceb6b64d4ad..5d3f680a57c 100644 --- a/spec/frontend/pipelines/components/dag/parsing_utils_spec.js +++ b/spec/frontend/pipelines/components/dag/parsing_utils_spec.js @@ -30,7 +30,10 @@ describe('DAG visualization parsing utilities', () => { { source: 'job2', target: 'job4' }, ]; - const dedupedLinks = [{ source: 'job1', target: 'job2' }, { source: 'job2', target: 'job4' }]; + const dedupedLinks = [ + { source: 'job1', target: 'job2' }, + { source: 'job2', target: 'job4' }, + ]; const nodeLookup = { job1: { diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js index 8a6586a7d7d..00fe9e784b3 100644 --- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js +++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js @@ -11,10 +11,10 @@ describe('Pipelines filtered search', () => { let mock; const findFilteredSearch = () => wrapper.find(GlFilteredSearch); - const getSearchToken = type => + const getSearchToken = (type) => findFilteredSearch() .props('availableTokens') - .find(token => token.type === type); + .find((token) => token.type === type); const findBranchToken = () => getSearchToken('ref'); const findTagToken = () => getSearchToken('tag'); const findUserToken = () => getSearchToken('username'); @@ -26,7 +26,7 @@ describe('Pipelines filtered search', () => { projectId: '21', params, }, - attachToDocument: true, + attachTo: document.body, }); }; diff --git a/spec/frontend/pipelines/empty_state_spec.js b/spec/frontend/pipelines/empty_state_spec.js index 28a73c8863c..7e42a3b5ae9 100644 --- a/spec/frontend/pipelines/empty_state_spec.js +++ b/spec/frontend/pipelines/empty_state_spec.js @@ -1,5 +1,7 @@ import { shallowMount } from '@vue/test-utils'; +import { withGonExperiment } from 'helpers/experimentation_helper'; import EmptyState from '~/pipelines/components/pipelines_list/empty_state.vue'; +import Tracking from '~/tracking'; describe('Pipelines Empty State', () => { let wrapper; @@ -38,15 +40,104 @@ describe('Pipelines Empty State', () => { expect(findGetStartedButton().attributes('href')).toBe('foo'); }); - it('should render empty state information', () => { - expect(findInfoText()).toContain( - 'Continuous Integration can help catch bugs by running your tests automatically', - 'while Continuous Deployment can help you deliver code to your product environment', - ); + describe('when in control group', () => { + it('should render empty state information', () => { + expect(findInfoText()).toContain( + 'Continuous Integration can help catch bugs by running your tests automatically', + 'while Continuous Deployment can help you deliver code to your product environment', + ); + }); + + it('should render a button', () => { + expect(findGetStartedButton().text()).toBe('Get started with Pipelines'); + }); + }); + + describe('when in experiment group', () => { + withGonExperiment('pipelinesEmptyState'); + + beforeEach(() => { + createWrapper(); + }); + + it('should render empty state information', () => { + expect(findInfoText()).toContain( + 'GitLab CI/CD can automatically build, test, and deploy your code. Let GitLab take care of time', + 'consuming tasks, so you can spend more time creating', + ); + }); + + it('should render button text', () => { + expect(findGetStartedButton().text()).toBe('Get started with CI/CD'); + }); }); - it('should render a button', () => { - expect(findGetStartedButton().text()).toBe('Get started with Pipelines'); + describe('tracking', () => { + let origGon; + + describe('when data is set', () => { + beforeEach(() => { + jest.spyOn(Tracking, 'event').mockImplementation(() => {}); + origGon = window.gon; + + window.gon = { + tracking_data: { + category: 'Growth::Activation::Experiment::PipelinesEmptyState', + value: 1, + property: 'experimental_group', + label: 'label', + }, + }; + createWrapper(); + }); + + afterEach(() => { + window.gon = origGon; + }); + + it('tracks when mounted', () => { + expect(Tracking.event).toHaveBeenCalledWith( + 'Growth::Activation::Experiment::PipelinesEmptyState', + 'viewed', + { + value: 1, + label: 'label', + property: 'experimental_group', + }, + ); + }); + + it('tracks when button is clicked', () => { + findGetStartedButton().vm.$emit('click'); + + expect(Tracking.event).toHaveBeenCalledWith( + 'Growth::Activation::Experiment::PipelinesEmptyState', + 'documentation_clicked', + { + value: 1, + label: 'label', + property: 'experimental_group', + }, + ); + }); + }); + + describe('when no data is defined', () => { + beforeEach(() => { + jest.spyOn(Tracking, 'event').mockImplementation(() => {}); + + createWrapper(); + }); + + it('does not track on view', () => { + expect(Tracking.event).not.toHaveBeenCalled(); + }); + + it('does not track when button is clicked', () => { + findGetStartedButton().vm.$emit('click'); + expect(Tracking.event).not.toHaveBeenCalled(); + }); + }); }); }); }); diff --git a/spec/frontend/pipelines/graph/action_component_spec.js b/spec/frontend/pipelines/graph/action_component_spec.js index ab477292bc1..95d96e127c6 100644 --- a/spec/frontend/pipelines/graph/action_component_spec.js +++ b/spec/frontend/pipelines/graph/action_component_spec.js @@ -33,7 +33,7 @@ describe('pipeline graph action component', () => { expect(wrapper.attributes('title')).toBe('bar'); }); - it('should update bootstrap tooltip when title changes', done => { + it('should update bootstrap tooltip when title changes', (done) => { wrapper.setProps({ tooltipText: 'changed' }); wrapper.vm @@ -51,7 +51,7 @@ describe('pipeline graph action component', () => { }); describe('on click', () => { - it('emits `pipelineActionRequestComplete` after a successful request', done => { + it('emits `pipelineActionRequestComplete` after a successful request', (done) => { jest.spyOn(wrapper.vm, '$emit'); findButton().trigger('click'); @@ -64,7 +64,7 @@ describe('pipeline graph action component', () => { .catch(done.fail); }); - it('renders a loading icon while waiting for request', done => { + it('renders a loading icon while waiting for request', (done) => { findButton().trigger('click'); wrapper.vm.$nextTick(() => { diff --git a/spec/frontend/pipelines/graph/graph_component_legacy_spec.js b/spec/frontend/pipelines/graph/graph_component_legacy_spec.js index 3b1909b6564..840b1f8baf5 100644 --- a/spec/frontend/pipelines/graph/graph_component_legacy_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_legacy_spec.js @@ -1,4 +1,4 @@ -import Vue from 'vue'; +import { nextTick } from 'vue'; import { mount } from '@vue/test-utils'; import { GlLoadingIcon } from '@gitlab/ui'; import { setHTMLFixture } from 'helpers/fixtures'; @@ -18,7 +18,7 @@ describe('graph component', () => { const findExpandPipelineBtn = () => wrapper.find('[data-testid="expand-pipeline-button"]'); const findAllExpandPipelineBtns = () => wrapper.findAll('[data-testid="expand-pipeline-button"]'); const findStageColumns = () => wrapper.findAll(StageColumnComponentLegacy); - const findStageColumnAt = i => findStageColumns().at(i); + const findStageColumnAt = (i) => findStageColumns().at(i); beforeEach(() => { mediator = new PipelinesMediator({ endpoint: '' }); @@ -104,11 +104,9 @@ describe('graph component', () => { }); it('should include the left-connector class in the build of the second child', () => { - expect( - findStageColumnAt(1) - .find('.build:nth-child(1)') - .classes('left-connector'), - ).toBe(true); + expect(findStageColumnAt(1).find('.build:nth-child(1)').classes('left-connector')).toBe( + true, + ); }); it('should include the js-has-linked-pipelines flag', () => { @@ -119,12 +117,7 @@ describe('graph component', () => { describe('computeds and methods', () => { describe('capitalizeStageName', () => { it('it capitalizes the stage name', () => { - expect( - wrapper - .findAll('.stage-column .stage-name') - .at(1) - .text(), - ).toBe('Prebuild'); + expect(wrapper.findAll('.stage-column .stage-name').at(1).text()).toBe('Prebuild'); }); }); @@ -160,21 +153,20 @@ describe('graph component', () => { describe('triggered by', () => { describe('on click', () => { - it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => { + it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', async () => { const btnWrapper = findExpandPipelineBtn(); btnWrapper.trigger('click'); - btnWrapper.vm.$nextTick(() => { - expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([ - store.state.pipeline.triggered_by, - ]); - }); + await nextTick(); + expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([ + store.state.pipeline.triggered_by, + ]); }); }); describe('with expanded pipeline', () => { - it('should render expanded pipeline', done => { + it('should render expanded pipeline', async () => { // expand the pipeline store.state.pipeline.triggered_by[0].isExpanded = true; @@ -186,40 +178,46 @@ describe('graph component', () => { }, }); - Vue.nextTick() - .then(() => { - expect(wrapper.find('.js-upstream-pipeline-12').exists()).toBe(true); - }) - .then(done) - .catch(done.fail); + await nextTick(); + expect(wrapper.find('.js-upstream-pipeline-12').exists()).toBe(true); }); }); }); describe('triggered', () => { describe('on click', () => { - it('should emit `onClickTriggered`', () => { - // We have to mock this method since we do both style change and - // emit and event, not mocking returns an error. - wrapper.setMethods({ - handleClickedDownstream: jest.fn(() => - wrapper.vm.$emit('onClickTriggered', ...store.state.pipeline.triggered), - ), + // We have to mock this property of HTMLElement since component relies on it + let offsetParentDescriptor; + beforeAll(() => { + offsetParentDescriptor = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetParent', + ); + Object.defineProperty(HTMLElement.prototype, 'offsetParent', { + get() { + return this.parentNode; + }, }); + }); + afterAll(() => { + Object.defineProperty(HTMLElement.prototype, offsetParentDescriptor); + }); + it('should emit `onClickDownstreamPipeline`', async () => { const btnWrappers = findAllExpandPipelineBtns(); const downstreamBtnWrapper = btnWrappers.at(btnWrappers.length - 1); downstreamBtnWrapper.trigger('click'); - downstreamBtnWrapper.vm.$nextTick(() => { - expect(wrapper.emitted().onClickTriggered).toEqual([store.state.pipeline.triggered]); - }); + await nextTick(); + expect(wrapper.emitted().onClickDownstreamPipeline).toEqual([ + [store.state.pipeline.triggered[1]], + ]); }); }); describe('with expanded pipeline', () => { - it('should render expanded pipeline', done => { + it('should render expanded pipeline', async () => { // expand the pipeline store.state.pipeline.triggered[0].isExpanded = true; @@ -231,12 +229,8 @@ describe('graph component', () => { }, }); - Vue.nextTick() - .then(() => { - expect(wrapper.find('.js-downstream-pipeline-34993051')).not.toBeNull(); - }) - .then(done) - .catch(done.fail); + await nextTick(); + expect(wrapper.find('.js-downstream-pipeline-34993051')).not.toBeNull(); }); }); diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js index 7572dd83798..cfc3b7af282 100644 --- a/spec/frontend/pipelines/graph/graph_component_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_spec.js @@ -1,7 +1,9 @@ import { mount, shallowMount } from '@vue/test-utils'; import PipelineGraph from '~/pipelines/components/graph/graph_component.vue'; import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; +import JobItem from '~/pipelines/components/graph/job_item.vue'; import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue'; +import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue'; import { GRAPHQL } from '~/pipelines/components/graph/constants'; import { generateResponse, @@ -13,21 +15,37 @@ describe('graph component', () => { let wrapper; const findLinkedColumns = () => wrapper.findAll(LinkedPipelinesColumn); + const findLinksLayer = () => wrapper.find(LinksLayer); const findStageColumns = () => wrapper.findAll(StageColumnComponent); const defaultProps = { pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'), }; - const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => { + const createComponent = ({ + data = {}, + mountFn = shallowMount, + props = {}, + stubOverride = {}, + } = {}) => { wrapper = mountFn(PipelineGraph, { propsData: { ...defaultProps, ...props, }, + data() { + return { ...data }; + }, provide: { dataMethod: GRAPHQL, }, + stubs: { + 'links-inner': true, + 'linked-pipeline': true, + 'job-item': true, + 'job-group-dropdown': true, + ...stubOverride, + }, }); }; @@ -45,17 +63,36 @@ describe('graph component', () => { expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length); }); + it('renders the links layer', () => { + expect(findLinksLayer().exists()).toBe(true); + }); + describe('when column requests a refresh', () => { beforeEach(() => { - findStageColumns() - .at(0) - .vm.$emit('refreshPipelineGraph'); + findStageColumns().at(0).vm.$emit('refreshPipelineGraph'); }); it('refreshPipelineGraph is emitted', () => { expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1); }); }); + + describe('when links are present', () => { + beforeEach(async () => { + createComponent({ + mountFn: mount, + stubOverride: { 'job-item': false }, + data: { hoveredJobName: 'test_a' }, + }); + findLinksLayer().vm.$emit('highlightedJobsChange', ['test_c', 'build_c']); + }); + + it('dims unrelated jobs', () => { + const unrelatedJob = wrapper.find(JobItem); + expect(findLinksLayer().emitted().highlightedJobsChange).toHaveLength(1); + expect(unrelatedJob.classes('gl-opacity-3')).toBe(true); + }); + }); }); describe('when linked pipelines are not present', () => { diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js index 875aaa48037..54593c527cb 100644 --- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js @@ -2,10 +2,10 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { shallowMount } from '@vue/test-utils'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; -import createMockApollo from 'jest/helpers/mock_apollo_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue'; import PipelineGraph from '~/pipelines/components/graph/graph_component.vue'; -import getPipelineDetails from '~/pipelines/graphql/queries/get_pipeline_details.query.graphql'; import { mockPipelineResponse } from './mock_data'; const defaultProvide = { diff --git a/spec/frontend/pipelines/graph/job_item_spec.js b/spec/frontend/pipelines/graph/job_item_spec.js index 8aabb2f9cdd..cb2837cbb39 100644 --- a/spec/frontend/pipelines/graph/job_item_spec.js +++ b/spec/frontend/pipelines/graph/job_item_spec.js @@ -7,7 +7,7 @@ describe('pipeline graph job item', () => { const findJobWithoutLink = () => wrapper.find('[data-testid="job-without-link"]'); const findJobWithLink = () => wrapper.find('[data-testid="job-with-link"]'); - const createWrapper = propsData => { + const createWrapper = (propsData) => { wrapper = mount(JobItem, { propsData, }); @@ -52,7 +52,7 @@ describe('pipeline graph job item', () => { }); describe('name with link', () => { - it('should render the job name and status with a link', done => { + it('should render the job name and status with a link', (done) => { createWrapper({ job: mockJob }); wrapper.vm.$nextTick(() => { diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js index 37eb5f900dd..6db152f2607 100644 --- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js @@ -1,10 +1,10 @@ import VueApollo from 'vue-apollo'; import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; -import createMockApollo from 'jest/helpers/mock_apollo_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; import PipelineGraph from '~/pipelines/components/graph/graph_component.vue'; import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue'; import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue'; -import getPipelineDetails from '~/pipelines/graphql/queries/get_pipeline_details.query.graphql'; import { DOWNSTREAM, GRAPHQL } from '~/pipelines/components/graph/constants'; import { LOAD_FAILURE } from '~/pipelines/constants'; import { diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js index d53a11eea0e..7650cbd2d5c 100644 --- a/spec/frontend/pipelines/graph/mock_data.js +++ b/spec/frontend/pipelines/graph/mock_data.js @@ -56,7 +56,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -96,7 +96,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -136,7 +136,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -176,7 +176,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -200,7 +200,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -224,7 +224,7 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [], }, }, @@ -277,18 +277,18 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [ { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_c', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_b', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl', }, @@ -331,26 +331,26 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [ { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 3/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 2/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 1/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_b', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl', }, @@ -377,26 +377,26 @@ export const mockPipelineResponse = { }, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [ { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 3/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 2/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_d 1/3', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_b', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl', }, @@ -433,18 +433,18 @@ export const mockPipelineResponse = { action: null, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [ { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_c', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_b', }, { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_a_nlfjkdnlvskfnksvjknlfdjvlvnjdkjdf_nvjkenjkrlngjeknjkl', }, @@ -481,10 +481,10 @@ export const mockPipelineResponse = { action: null, }, needs: { - __typename: 'CiJobConnection', + __typename: 'CiBuildNeedConnection', nodes: [ { - __typename: 'CiJob', + __typename: 'CiBuildNeed', name: 'build_b', }, ], @@ -578,41 +578,54 @@ export const upstream = { export const wrappedPipelineReturn = { data: { project: { + __typename: 'Project', pipeline: { + __typename: 'Pipeline', id: 'gid://gitlab/Ci::Pipeline/175', iid: '38', downstream: { + __typename: 'PipelineConnection', nodes: [], }, upstream: { id: 'gid://gitlab/Ci::Pipeline/174', iid: '37', path: '/root/elemenohpee/-/pipelines/174', + __typename: 'Pipeline', status: { + __typename: 'DetailedStatus', group: 'success', label: 'passed', icon: 'status_success', }, sourceJob: { name: 'test_c', + __typename: 'CiJob', }, project: { id: 'gid://gitlab/Project/25', name: 'elemenohpee', fullPath: 'root/elemenohpee', + __typename: 'Project', }, }, stages: { + __typename: 'CiStageConnection', nodes: [ { name: 'build', + __typename: 'CiStage', status: { action: null, + __typename: 'DetailedStatus', }, groups: { + __typename: 'CiGroupConnection', nodes: [ { + __typename: 'CiGroup', status: { + __typename: 'DetailedStatus', label: 'passed', group: 'success', icon: 'status_success', @@ -620,20 +633,25 @@ export const wrappedPipelineReturn = { name: 'build_n', size: 1, jobs: { + __typename: 'CiJobConnection', nodes: [ { + __typename: 'CiJob', name: 'build_n', scheduledAt: null, needs: { + __typename: 'CiBuildNeedConnection', nodes: [], }, status: { + __typename: 'DetailedStatus', icon: 'status_success', tooltip: 'passed', hasDetails: true, detailsPath: '/root/elemenohpee/-/jobs/1662', group: 'success', action: { + __typename: 'StatusAction', buttonTitle: 'Retry this job', icon: 'retry', path: '/root/elemenohpee/-/jobs/1662/retry', @@ -656,7 +674,7 @@ export const wrappedPipelineReturn = { export const generateResponse = (raw, mockPath) => unwrapPipelineData(mockPath, raw.data); -export const pipelineWithUpstreamDownstream = base => { +export const pipelineWithUpstreamDownstream = (base) => { const pip = { ...base }; pip.data.project.pipeline.downstream = downstream; pip.data.project.pipeline.upstream = upstream; diff --git a/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js b/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js index 463e4c12c7d..2965325ea7c 100644 --- a/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js +++ b/spec/frontend/pipelines/graph/stage_column_component_legacy_spec.js @@ -40,12 +40,7 @@ describe('stage column component', () => { }); it('should render provided title', () => { - expect( - wrapper - .find('.stage-name') - .text() - .trim(), - ).toBe('foo'); + expect(wrapper.find('.stage-name').text().trim()).toBe('foo'); }); it('should render the provided groups', () => { diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js index 44803929f6d..202e25ccda3 100644 --- a/spec/frontend/pipelines/graph/stage_column_component_spec.js +++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js @@ -30,6 +30,7 @@ const mockGroups = Array(4) const defaultProps = { title: 'Fish', groups: mockGroups, + pipelineId: 159, }; describe('stage column component', () => { @@ -92,36 +93,51 @@ describe('stage column component', () => { }); describe('job', () => { - beforeEach(() => { - createComponent({ - method: mount, - props: { - groups: [ - { - id: 4259, - name: '<img src=x onerror=alert(document.domain)>', - status: { - icon: 'status_success', - label: 'success', - tooltip: '<img src=x onerror=alert(document.domain)>', + describe('text handling', () => { + beforeEach(() => { + createComponent({ + method: mount, + props: { + groups: [ + { + id: 4259, + name: '<img src=x onerror=alert(document.domain)>', + status: { + icon: 'status_success', + label: 'success', + tooltip: '<img src=x onerror=alert(document.domain)>', + }, }, - }, - ], - title: 'test <img src=x onerror=alert(document.domain)>', - }, + ], + title: 'test <img src=x onerror=alert(document.domain)>', + }, + }); }); - }); - it('capitalizes and escapes name', () => { - expect(findStageColumnTitle().text()).toBe( - 'Test <img src=x onerror=alert(document.domain)>', - ); + it('capitalizes and escapes name', () => { + expect(findStageColumnTitle().text()).toBe( + 'Test <img src=x onerror=alert(document.domain)>', + ); + }); + + it('escapes id', () => { + expect(findStageColumnGroup().attributes('id')).toBe( + 'ci-badge-<img src=x onerror=alert(document.domain)>', + ); + }); }); - it('escapes id', () => { - expect(findStageColumnGroup().attributes('id')).toBe( - 'ci-badge-<img src=x onerror=alert(document.domain)>', - ); + describe('interactions', () => { + beforeEach(() => { + createComponent({ method: mount }); + }); + + it('emits jobHovered event on mouseenter and mouseleave', async () => { + await findStageColumnGroup().trigger('mouseenter'); + expect(wrapper.emitted().jobHover).toEqual([[defaultProps.groups[0].name]]); + await findStageColumnGroup().trigger('mouseleave'); + expect(wrapper.emitted().jobHover).toEqual([[defaultProps.groups[0].name], ['']]); + }); }); }); diff --git a/spec/frontend/pipelines/nav_controls_spec.js b/spec/frontend/pipelines/nav_controls_spec.js index 139d53881c8..305dc557b39 100644 --- a/spec/frontend/pipelines/nav_controls_spec.js +++ b/spec/frontend/pipelines/nav_controls_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; +import mountComponent from 'helpers/vue_mount_component_helper'; import navControlsComp from '~/pipelines/components/pipelines_list/nav_controls.vue'; -import mountComponent from '../helpers/vue_mount_component_helper'; describe('Pipelines Nav Controls', () => { let NavControlsComponent; diff --git a/spec/frontend/pipelines/pipeline_graph/mock_data.js b/spec/frontend/pipelines/pipeline_graph/mock_data.js index a77973b293c..7d1a7a79c7f 100644 --- a/spec/frontend/pipelines/pipeline_graph/mock_data.js +++ b/spec/frontend/pipelines/pipeline_graph/mock_data.js @@ -1,4 +1,4 @@ -import { createUniqueLinkId } from '~/pipelines/utils'; +import { createUniqueLinkId } from '~/pipelines/components/graph_shared/drawing_utils'; export const yamlString = `stages: - empty @@ -78,18 +78,12 @@ export const pipelineData = { groups: [ { name: 'deploy_1', - jobs: [{ script: 'yarn magick', stage: 'deploy' }], + jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['test_1'] }], id: jobId4, }, ], }, ], - jobs: { - [jobId1]: {}, - [jobId2]: {}, - [jobId3]: {}, - [jobId4]: {}, - }, }; export const singleStageData = { diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js index 6704ee06c1a..b6b0a964383 100644 --- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js +++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js @@ -1,7 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import { GlAlert } from '@gitlab/ui'; import { pipelineData, singleStageData } from './mock_data'; -import { CI_CONFIG_STATUS_INVALID } from '~/pipeline_editor/constants'; +import { CI_CONFIG_STATUS_INVALID, CI_CONFIG_STATUS_VALID } from '~/pipeline_editor/constants'; import { DRAW_FAILURE, EMPTY_PIPELINE_DATA, INVALID_CI_CONFIG } from '~/pipelines/constants'; import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; import StagePill from '~/pipelines/components/pipeline_graph/stage_pill.vue'; @@ -23,7 +23,7 @@ describe('pipeline graph component', () => { const findAlert = () => wrapper.find(GlAlert); const findAllStagePills = () => wrapper.findAll(StagePill); const findAllStageBackgroundElements = () => wrapper.findAll('[data-testid="stage-background"]'); - const findStageBackgroundElementAt = index => findAllStageBackgroundElements().at(index); + const findStageBackgroundElementAt = (index) => findAllStageBackgroundElements().at(index); const findAllJobPills = () => wrapper.findAll(JobPill); afterEach(() => { @@ -37,7 +37,7 @@ describe('pipeline graph component', () => { }); it('renders an empty section', () => { - expect(wrapper.text()).toBe(wrapper.vm.$options.warningTexts[EMPTY_PIPELINE_DATA]); + expect(wrapper.text()).toBe(wrapper.vm.$options.errorTexts[EMPTY_PIPELINE_DATA]); expect(findPipelineGraph().exists()).toBe(false); expect(findAllStagePills()).toHaveLength(0); expect(findAllJobPills()).toHaveLength(0); @@ -51,23 +51,25 @@ describe('pipeline graph component', () => { it('renders an error message and does not render the graph', () => { expect(findAlert().exists()).toBe(true); - expect(findAlert().text()).toBe(wrapper.vm.$options.warningTexts[INVALID_CI_CONFIG]); + expect(findAlert().text()).toBe(wrapper.vm.$options.errorTexts[INVALID_CI_CONFIG]); expect(findPipelineGraph().exists()).toBe(false); }); }); - describe('without `INVALID` status', () => { + describe('with `VALID` status', () => { beforeEach(() => { - wrapper = createComponent(); + wrapper = createComponent({ + pipelineData: { status: CI_CONFIG_STATUS_VALID, stages: [{ name: 'hello', groups: [] }] }, + }); }); it('renders the graph with no status error', () => { - expect(findAlert().text()).not.toBe(wrapper.vm.$options.warningTexts[INVALID_CI_CONFIG]); + expect(findAlert().exists()).toBe(false); expect(findPipelineGraph().exists()).toBe(true); }); }); - describe('with error while rendering the links', () => { + describe('with error while rendering the links with needs', () => { beforeEach(() => { wrapper = createComponent(); }); diff --git a/spec/frontend/pipelines/pipeline_graph/utils_spec.js b/spec/frontend/pipelines/pipeline_graph/utils_spec.js index 12154df6fcf..070d3bf7dac 100644 --- a/spec/frontend/pipelines/pipeline_graph/utils_spec.js +++ b/spec/frontend/pipelines/pipeline_graph/utils_spec.js @@ -68,10 +68,10 @@ describe('utils functions', () => { it('returns a hash with the jobname as key and all its data as value', () => { const jobs = { - [jobName1]: job1, - [jobName2]: job2, - [jobName3]: job3, - [jobName4]: job4, + [jobName1]: { jobs: [job1], name: jobName1, needs: [] }, + [jobName2]: { jobs: [job2], name: jobName2, needs: [] }, + [jobName3]: { jobs: [job3], name: jobName3, needs: job3.needs }, + [jobName4]: { jobs: [job4], name: jobName4, needs: job4.needs }, }; expect(createJobsHash(pipelineGraphData.stages)).toEqual(jobs); @@ -110,5 +110,41 @@ describe('utils functions', () => { [jobName4]: [jobName3, jobName1, jobName2], }); }); + + it('handles parallel jobs by adding the group name as a need', () => { + const size = 3; + const jobOptimize1 = 'optimize_1'; + const jobPrepareA = 'prepare_a'; + const jobPrepareA1 = `${jobPrepareA} 1/${size}`; + const jobPrepareA2 = `${jobPrepareA} 2/${size}`; + const jobPrepareA3 = `${jobPrepareA} 3/${size}`; + + const jobsParallel = { + [jobOptimize1]: { + jobs: [job1], + name: [jobOptimize1], + needs: [jobPrepareA1, jobPrepareA2, jobPrepareA3], + }, + [jobPrepareA]: { jobs: [], name: jobPrepareA, needs: [], size }, + [jobPrepareA1]: { jobs: [], name: jobPrepareA, needs: [], size }, + [jobPrepareA2]: { jobs: [], name: jobPrepareA, needs: [], size }, + [jobPrepareA3]: { jobs: [], name: jobPrepareA, needs: [], size }, + }; + + expect(generateJobNeedsDict(jobsParallel)).toEqual({ + [jobOptimize1]: [ + jobPrepareA1, + // This is the important part, the `jobPrepareA` group name has been + // added to our list of needs. + jobPrepareA, + jobPrepareA2, + jobPrepareA3, + ], + [jobPrepareA]: [], + [jobPrepareA1]: [], + [jobPrepareA2]: [], + [jobPrepareA3]: [], + }); + }); }); }); diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js index fc45af2c254..47315bd42e6 100644 --- a/spec/frontend/pipelines/pipeline_url_spec.js +++ b/spec/frontend/pipelines/pipeline_url_spec.js @@ -28,7 +28,7 @@ describe('Pipeline Url Component', () => { pipelineScheduleUrl: 'foo', }; - const createComponent = props => { + const createComponent = (props) => { wrapper = shallowMount(PipelineUrlComponent, { propsData: { ...defaultProps, ...props }, provide: { diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js index 071a2b24889..69c1b7ce43d 100644 --- a/spec/frontend/pipelines/pipelines_actions_spec.js +++ b/spec/frontend/pipelines/pipelines_actions_spec.js @@ -55,11 +55,7 @@ describe('Pipelines Actions dropdown', () => { }); it("renders a disabled action when it's not playable", () => { - expect( - findAllDropdownItems() - .at(1) - .attributes('disabled'), - ).toBe('true'); + expect(findAllDropdownItems().at(1).attributes('disabled')).toBe('true'); }); describe('on click', () => { @@ -100,9 +96,7 @@ describe('Pipelines Actions dropdown', () => { mock.onPost(scheduledJobAction.path).reply(200); jest.spyOn(window, 'confirm').mockReturnValue(true); - findAllDropdownItems() - .at(0) - .vm.$emit('click'); + findAllDropdownItems().at(0).vm.$emit('click'); expect(window.confirm).toHaveBeenCalled(); @@ -115,28 +109,20 @@ describe('Pipelines Actions dropdown', () => { mock.onPost(scheduledJobAction.path).reply(200); jest.spyOn(window, 'confirm').mockReturnValue(false); - findAllDropdownItems() - .at(0) - .vm.$emit('click'); + findAllDropdownItems().at(0).vm.$emit('click'); expect(window.confirm).toHaveBeenCalled(); expect(mock.history.post.length).toBe(0); }); it('displays the remaining time in the dropdown', () => { - expect( - findAllCountdowns() - .at(0) - .props('endDateString'), - ).toBe(scheduledJobAction.scheduled_at); + expect(findAllCountdowns().at(0).props('endDateString')).toBe( + scheduledJobAction.scheduled_at, + ); }); it('displays 00:00:00 for expired jobs in the dropdown', () => { - expect( - findAllCountdowns() - .at(1) - .props('endDateString'), - ).toBe(expiredJobAction.scheduled_at); + expect(findAllCountdowns().at(1).props('endDateString')).toBe(expiredJobAction.scheduled_at); }); }); }); diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js index 83f6cb68eba..4f4c15fd4cc 100644 --- a/spec/frontend/pipelines/pipelines_artifacts_spec.js +++ b/spec/frontend/pipelines/pipelines_artifacts_spec.js @@ -1,12 +1,12 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlLink } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; describe('Pipelines Artifacts dropdown', () => { let wrapper; const createComponent = () => { - wrapper = shallowMount(PipelineArtifacts, { + wrapper = mount(PipelineArtifacts, { propsData: { artifacts: [ { @@ -22,8 +22,8 @@ describe('Pipelines Artifacts dropdown', () => { }); }; - const findGlLink = () => wrapper.find(GlLink); - const findAllGlLinks = () => wrapper.find('.dropdown-menu').findAll(GlLink); + const findFirstGlDropdownItem = () => wrapper.find(GlDropdownItem); + const findAllGlDropdownItems = () => wrapper.find(GlDropdown).findAll(GlDropdownItem); beforeEach(() => { createComponent(); @@ -35,12 +35,12 @@ describe('Pipelines Artifacts dropdown', () => { }); it('should render a dropdown with all the provided artifacts', () => { - expect(findAllGlLinks()).toHaveLength(2); + expect(findAllGlDropdownItems()).toHaveLength(2); }); it('should render a link with the provided path', () => { - expect(findGlLink().attributes('href')).toEqual('/download/path'); + expect(findFirstGlDropdownItem().find('a').attributes('href')).toEqual('/download/path'); - expect(findGlLink().text()).toContain('artifact'); + expect(findFirstGlDropdownItem().text()).toContain('artifact'); }); }); diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js index ce0e76ba22d..5d82669b0b8 100644 --- a/spec/frontend/pipelines/pipelines_spec.js +++ b/spec/frontend/pipelines/pipelines_spec.js @@ -1,3 +1,4 @@ +import { nextTick } from 'vue'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; @@ -57,10 +58,10 @@ describe('Pipelines', () => { }; const findFilteredSearch = () => wrapper.find(GlFilteredSearch); - const findByTestId = id => wrapper.find(`[data-testid="${id}"]`); + const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`); const findNavigationTabs = () => wrapper.find(NavigationTabs); const findNavigationControls = () => wrapper.find(NavigationControls); - const findTab = tab => findByTestId(`pipelines-tab-${tab}`); + const findTab = (tab) => findByTestId(`pipelines-tab-${tab}`); const findRunPipelineButton = () => findByTestId('run-pipeline-button'); const findCiLintButton = () => findByTestId('ci-lint-button'); @@ -72,7 +73,7 @@ describe('Pipelines', () => { const findTablePagination = () => wrapper.find(TablePagination); - const createComponent = (props = defaultProps, methods) => { + const createComponent = (props = defaultProps) => { wrapper = mount(PipelinesComponent, { propsData: { store: new Store(), @@ -80,13 +81,15 @@ describe('Pipelines', () => { params: {}, ...props, }, - methods: { - ...methods, - }, }); }; beforeEach(() => { + delete window.location; + }); + + beforeEach(() => { + window.location = { search: '' }; mock = new MockAdapter(axios); pipelines = getJSONFixture(jsonFixtureName); @@ -170,7 +173,7 @@ describe('Pipelines', () => { it('renders tab empty state finished scope', () => { wrapper.vm.scope = 'finished'; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findBlankState().text()).toBe('There are currently no finished pipelines.'); }); }); @@ -194,16 +197,8 @@ describe('Pipelines', () => { }); it('renders empty state', () => { - expect( - findEmptyState() - .find('h4') - .text(), - ).toBe('Build with confidence'); - expect( - findEmptyState() - .find(GlButton) - .attributes('href'), - ).toBe(paths.helpPagePath); + expect(findEmptyState().find('h4').text()).toBe('Build with confidence'); + expect(findEmptyState().find(GlButton).attributes('href')).toBe(paths.helpPagePath); }); it('does not render tabs nor buttons', () => { @@ -320,11 +315,7 @@ describe('Pipelines', () => { 'This project is not currently set up to run pipelines.', ); - expect( - findEmptyState() - .find(GlButton) - .exists(), - ).toBeFalsy(); + expect(findEmptyState().find(GlButton).exists()).toBeFalsy(); }); it('does not render tabs or buttons', () => { @@ -394,30 +385,23 @@ describe('Pipelines', () => { }); it('should make an API request when using tabs', () => { - const updateContentMock = jest.fn(() => {}); - createComponent( - { hasGitlabCi: true, canCreatePipeline: true, ...paths }, - { - updateContent: updateContentMock, - }, - ); + createComponent({ hasGitlabCi: true, canCreatePipeline: true, ...paths }); + jest.spyOn(wrapper.vm.service, 'getPipelines'); return waitForPromises().then(() => { findTab('finished').trigger('click'); - expect(updateContentMock).toHaveBeenCalledWith({ scope: 'finished', page: '1' }); + expect(wrapper.vm.service.getPipelines).toHaveBeenCalledWith({ + scope: 'finished', + page: '1', + }); }); }); describe('with pagination', () => { it('should make an API request when using pagination', () => { - const updateContentMock = jest.fn(() => {}); - createComponent( - { hasGitlabCi: true, canCreatePipeline: true, ...paths }, - { - updateContent: updateContentMock, - }, - ); + createComponent({ hasGitlabCi: true, canCreatePipeline: true, ...paths }); + jest.spyOn(wrapper.vm.service, 'getPipelines'); return waitForPromises() .then(() => { @@ -430,12 +414,14 @@ describe('Pipelines', () => { totalPages: 5, }; - return wrapper.vm.$nextTick(); + return nextTick(); }) .then(() => { wrapper.find('.next-page-item').trigger('click'); - - expect(updateContentMock).toHaveBeenCalledWith({ scope: 'all', page: '2' }); + expect(wrapper.vm.service.getPipelines).toHaveBeenCalledWith({ + scope: 'all', + page: '2', + }); }); }); }); @@ -554,7 +540,7 @@ describe('Pipelines', () => { wrapper.vm.hasError = true; wrapper.vm.isLoading = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findBlankState().props('message')).toBe( 'There was an error fetching the pipelines. Try again in a few moments or contact your support team.', ); @@ -566,7 +552,7 @@ describe('Pipelines', () => { wrapper.vm.hasError = false; wrapper.vm.state.pipelines = pipelines.pipelines; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(wrapper.find(PipelinesTableComponent).exists()).toBe(true); }); }); @@ -575,7 +561,7 @@ describe('Pipelines', () => { wrapper.vm.state.count.all = 10; wrapper.vm.isLoading = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findBlankState().exists()).toBe(true); expect(findBlankState().props('message')).toBe('There are currently no pipelines.'); }); @@ -584,7 +570,7 @@ describe('Pipelines', () => { it('shows empty tab when project has CI', () => { wrapper.vm.isLoading = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findBlankState().exists()).toBe(true); expect(findBlankState().props('message')).toBe('There are currently no pipelines.'); }); @@ -595,7 +581,7 @@ describe('Pipelines', () => { wrapper.vm.isLoading = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(wrapper.find(EmptyState).exists()).toBe(true); }); }); @@ -606,7 +592,7 @@ describe('Pipelines', () => { wrapper.vm.isLoading = true; wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(true); }); }); @@ -616,7 +602,7 @@ describe('Pipelines', () => { wrapper.vm.state.pipelines = pipelines.pipelines; wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(true); }); }); @@ -626,7 +612,7 @@ describe('Pipelines', () => { wrapper.vm.hasError = true; wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(true); }); }); @@ -636,7 +622,7 @@ describe('Pipelines', () => { wrapper.vm.state.count.all = 10; wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(true); }); }); @@ -644,7 +630,7 @@ describe('Pipelines', () => { it('returns false when has not made first request', () => { wrapper.vm.hasMadeRequest = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(false); }); }); @@ -655,7 +641,7 @@ describe('Pipelines', () => { wrapper.vm.isLoading = false; wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationTabs().exists()).toBe(false); }); }); @@ -665,7 +651,7 @@ describe('Pipelines', () => { it('returns true when it has paths & has made the first request', () => { wrapper.vm.hasMadeRequest = true; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationControls().exists()).toBe(true); }); }); @@ -673,7 +659,7 @@ describe('Pipelines', () => { it('returns false when it has not made the first request', () => { wrapper.vm.hasMadeRequest = false; - return wrapper.vm.$nextTick().then(() => { + return nextTick().then(() => { expect(findNavigationControls().exists()).toBe(false); }); }); @@ -692,7 +678,7 @@ describe('Pipelines', () => { return waitForPromises(); }); - it('updates request data and query params on filter submit', () => { + it('updates request data and query params on filter submit', async () => { const expectedQueryParams = { page: '1', scope: 'all', @@ -702,15 +688,17 @@ describe('Pipelines', () => { }; findFilteredSearch().vm.$emit('submit', mockSearch); + await nextTick(); expect(wrapper.vm.requestData).toEqual(expectedQueryParams); expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams); }); - it('does not add query params if raw text search is used', () => { + it('does not add query params if raw text search is used', async () => { const expectedQueryParams = { page: '1', scope: 'all' }; findFilteredSearch().vm.$emit('submit', ['rawText']); + await nextTick(); expect(wrapper.vm.requestData).toEqual(expectedQueryParams); expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams); diff --git a/spec/frontend/pipelines/pipelines_store_spec.js b/spec/frontend/pipelines/pipelines_store_spec.js index ce21f788ed5..f374ecd0c0a 100644 --- a/spec/frontend/pipelines/pipelines_store_spec.js +++ b/spec/frontend/pipelines/pipelines_store_spec.js @@ -21,7 +21,10 @@ describe('Pipelines Store', () => { }); it('should store the provided array', () => { - const array = [{ id: 1, status: 'running' }, { id: 2, status: 'success' }]; + const array = [ + { id: 1, status: 'running' }, + { id: 2, status: 'success' }, + ]; store.storePipelines(array); expect(store.state.pipelines).toEqual(array); diff --git a/spec/frontend/pipelines/pipelines_table_row_spec.js b/spec/frontend/pipelines/pipelines_table_row_spec.js index 32d53c0f1f8..9cdd24b2ab5 100644 --- a/spec/frontend/pipelines/pipelines_table_row_spec.js +++ b/spec/frontend/pipelines/pipelines_table_row_spec.js @@ -5,7 +5,7 @@ import eventHub from '~/pipelines/event_hub'; describe('Pipelines Table Row', () => { const jsonFixtureName = 'pipelines/pipelines.json'; - const createWrapper = pipeline => + const createWrapper = (pipeline) => mount(PipelinesTableRowComponent, { propsData: { pipeline, @@ -24,9 +24,9 @@ describe('Pipelines Table Row', () => { beforeEach(() => { const { pipelines } = getJSONFixture(jsonFixtureName); - pipeline = pipelines.find(p => p.user !== null && p.commit !== null); - pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null); - pipelineWithoutCommit = pipelines.find(p => p.user === null && p.commit === null); + pipeline = pipelines.find((p) => p.user !== null && p.commit !== null); + pipelineWithoutAuthor = pipelines.find((p) => p.user === null && p.commit !== null); + pipelineWithoutCommit = pipelines.find((p) => p.user === null && p.commit === null); }); afterEach(() => { @@ -82,10 +82,7 @@ describe('Pipelines Table Row', () => { ).toEqual(pipeline.user.path); expect( - wrapper - .find('.table-section:nth-child(3) .js-user-avatar-image-toolip') - .text() - .trim(), + wrapper.find('.table-section:nth-child(3) .js-user-avatar-image-tooltip').text().trim(), ).toEqual(pipeline.user.name); }); }); @@ -112,7 +109,7 @@ describe('Pipelines Table Row', () => { const commitAuthorLink = commitAuthorElement.attributes('href'); const commitAuthorName = commitAuthorElement - .find('.js-user-avatar-image-toolip') + .find('.js-user-avatar-image-tooltip') .text() .trim(); @@ -190,7 +187,7 @@ describe('Pipelines Table Row', () => { }); it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => { - eventHub.$on('retryPipeline', endpoint => { + eventHub.$on('retryPipeline', (endpoint) => { expect(endpoint).toBe('/retry'); }); @@ -199,7 +196,7 @@ describe('Pipelines Table Row', () => { }); it('emits `openConfirmationModal` event when cancel button is clicked and toggles loading', () => { - eventHub.$once('openConfirmationModal', data => { + eventHub.$once('openConfirmationModal', (data) => { const { id, ref, commit } = pipeline; expect(data.endpoint).toBe('/cancel'); @@ -215,7 +212,7 @@ describe('Pipelines Table Row', () => { wrapper.find('.js-pipelines-cancel-button').trigger('click'); }); - it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => { + it('renders a loading icon when `cancelingPipeline` matches pipeline id', (done) => { wrapper.setProps({ cancelingPipeline: pipeline.id }); wrapper.vm .$nextTick() diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index c7d104bbde8..fd73d507919 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -24,7 +24,7 @@ describe('Pipelines Table', () => { beforeEach(() => { const { pipelines } = getJSONFixture(jsonFixtureName); - pipeline = pipelines.find(p => p.user !== null && p.commit !== null); + pipeline = pipelines.find((p) => p.user !== null && p.commit !== null); createComponent(); }); diff --git a/spec/frontend/pipelines/shared/links_layer_spec.js b/spec/frontend/pipelines/shared/links_layer_spec.js new file mode 100644 index 00000000000..9ef5233dbce --- /dev/null +++ b/spec/frontend/pipelines/shared/links_layer_spec.js @@ -0,0 +1,99 @@ +import { mount, shallowMount } from '@vue/test-utils'; +import { GlAlert, GlButton } from '@gitlab/ui'; +import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue'; +import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue'; +import { generateResponse, mockPipelineResponse } from '../graph/mock_data'; + +describe('links layer component', () => { + let wrapper; + + const findAlert = () => wrapper.find(GlAlert); + const findShowAnyways = () => findAlert().find(GlButton); + const findLinksInner = () => wrapper.find(LinksInner); + + const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo'); + const containerId = `pipeline-links-container-${pipeline.id}`; + const slotContent = "<div>Ceci n'est pas un graphique</div>"; + + const tooManyStages = Array(101) + .fill(0) + .flatMap(() => pipeline.stages); + + const defaultProps = { + containerId, + containerMeasurements: { width: 400, height: 400 }, + pipelineId: pipeline.id, + pipelineData: pipeline.stages, + }; + + const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => { + wrapper = mountFn(LinksLayer, { + propsData: { + ...defaultProps, + ...props, + }, + slots: { + default: slotContent, + }, + stubs: { + 'links-inner': true, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('with data under max stages', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders the default slot', () => { + expect(wrapper.html()).toContain(slotContent); + }); + + it('renders the inner links component', () => { + expect(findLinksInner().exists()).toBe(true); + }); + }); + + describe('with more than the max number of stages', () => { + describe('rendering', () => { + beforeEach(() => { + createComponent({ props: { pipelineData: tooManyStages } }); + }); + + it('renders the default slot', () => { + expect(wrapper.html()).toContain(slotContent); + }); + + it('renders the alert component', () => { + expect(findAlert().exists()).toBe(true); + }); + + it('does not render the inner links component', () => { + expect(findLinksInner().exists()).toBe(false); + }); + }); + + describe('interactions', () => { + beforeEach(() => { + createComponent({ mountFn: mount, props: { pipelineData: tooManyStages } }); + }); + + it('renders the disable button', () => { + expect(findShowAnyways().exists()).toBe(true); + expect(findShowAnyways().text()).toBe(wrapper.vm.$options.i18n.showLinksAnyways); + }); + + it('shows links when override is clicked', async () => { + expect(findLinksInner().exists()).toBe(false); + await findShowAnyways().trigger('click'); + expect(findLinksInner().exists()).toBe(true); + }); + }); + }); +}); diff --git a/spec/frontend/pipelines/stage_spec.js b/spec/frontend/pipelines/stage_spec.js index e134b81856b..e4782a1dab1 100644 --- a/spec/frontend/pipelines/stage_spec.js +++ b/spec/frontend/pipelines/stage_spec.js @@ -1,6 +1,6 @@ +import 'bootstrap/js/dist/dropdown'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import StageComponent from '~/pipelines/components/pipelines_list/stage.vue'; import eventHub from '~/pipelines/event_hub'; @@ -22,8 +22,11 @@ describe('Pipelines stage component', () => { updateDropdown: false, }; + const isDropdownOpen = () => wrapper.classes('show'); + const createComponent = (props = {}) => { wrapper = mount(StageComponent, { + attachTo: document.body, propsData: { ...defaultProps, ...props, @@ -60,38 +63,29 @@ describe('Pipelines stage component', () => { createComponent(); }); - it('should render the received data and emit `clickedDropdown` event', () => { + it('should render the received data and emit `clickedDropdown` event', async () => { jest.spyOn(eventHub, '$emit'); wrapper.find('button').trigger('click'); - return waitForPromises().then(() => { - expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( - stageReply.latest_statuses[0].name, - ); + await axios.waitForAll(); + expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( + stageReply.latest_statuses[0].name, + ); - expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); - }); + expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); }); }); - describe('when request fails', () => { - beforeEach(() => { - mock.onGet('path.json').reply(500); - createComponent(); - }); + it('when request fails should close the dropdown', async () => { + mock.onGet('path.json').reply(500); + createComponent(); + wrapper.find({ ref: 'dropdown' }).trigger('click'); + expect(isDropdownOpen()).toBe(true); - it('should close the dropdown', () => { - wrapper.setMethods({ - closeDropdown: jest.fn(), - isDropdownOpen: jest.fn().mockReturnValue(false), - }); + wrapper.find('button').trigger('click'); + await axios.waitForAll(); - wrapper.find('button').trigger('click'); - - return waitForPromises().then(() => { - expect(wrapper.vm.closeDropdown).toHaveBeenCalled(); - }); - }); + expect(isDropdownOpen()).toBe(false); }); describe('update endpoint correctly', () => { @@ -109,47 +103,38 @@ describe('Pipelines stage component', () => { dropdown_path: 'bar.json', }, }); + return axios.waitForAll(); }); - it('should update the stage to request the new endpoint provided', () => { - return wrapper.vm - .$nextTick() - .then(() => { - wrapper.find('button').trigger('click'); - return waitForPromises(); - }) - .then(() => { - expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( - 'this is the updated content', - ); - }); + it('should update the stage to request the new endpoint provided', async () => { + wrapper.find('button').trigger('click'); + await axios.waitForAll(); + + expect(wrapper.find('.js-builds-dropdown-container ul').text()).toContain( + 'this is the updated content', + ); }); }); describe('pipelineActionRequestComplete', () => { beforeEach(() => { mock.onGet('path.json').reply(200, stageReply); - mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200); createComponent({ type: 'PIPELINES_TABLE' }); }); describe('within pipeline table', () => { - it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', () => { + it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', async () => { jest.spyOn(eventHub, '$emit'); wrapper.find('button').trigger('click'); + await axios.waitForAll(); - return waitForPromises() - .then(() => { - wrapper.find('.js-ci-action').trigger('click'); + wrapper.find('.js-ci-action').trigger('click'); + await axios.waitForAll(); - return waitForPromises(); - }) - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); - }); + expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); }); }); }); diff --git a/spec/frontend/pipelines/stores/pipeline_store_spec.js b/spec/frontend/pipelines/stores/pipeline_store_spec.js index 68d438109b3..2daf7e4b324 100644 --- a/spec/frontend/pipelines/stores/pipeline_store_spec.js +++ b/spec/frontend/pipelines/stores/pipeline_store_spec.js @@ -32,14 +32,14 @@ describe('EE Pipeline store', () => { describe('triggered', () => { it('adds isExpanding & isLoading keys set to false for each triggered pipeline', () => { - store.state.pipeline.triggered.forEach(pipeline => { + store.state.pipeline.triggered.forEach((pipeline) => { expect(pipeline.isExpanded).toEqual(false); expect(pipeline.isLoading).toEqual(false); }); }); it('parses nested triggered pipelines', () => { - store.state.pipeline.triggered[1].triggered.forEach(pipeline => { + store.state.pipeline.triggered[1].triggered.forEach((pipeline) => { expect(pipeline.isExpanded).toEqual(false); expect(pipeline.isLoading).toEqual(false); }); diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index 1809f15a6e6..f7ff36c0a46 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -1,10 +1,10 @@ import MockAdapter from 'axios-mock-adapter'; import { getJSONFixture } from 'helpers/fixtures'; +import { TEST_HOST } from 'helpers/test_constants'; +import testAction from 'helpers/vuex_action_helper'; import axios from '~/lib/utils/axios_utils'; import * as actions from '~/pipelines/stores/test_reports/actions'; import * as types from '~/pipelines/stores/test_reports/mutation_types'; -import { TEST_HOST } from '../../../helpers/test_constants'; -import testAction from '../../../helpers/vuex_action_helper'; import { deprecatedCreateFlash as createFlash } from '~/flash'; jest.mock('~/flash.js'); @@ -39,7 +39,7 @@ describe('Actions TestReports Store', () => { mock.onGet(summaryEndpoint).replyOnce(200, summary, {}); }); - it('sets testReports and shows tests', done => { + it('sets testReports and shows tests', (done) => { testAction( actions.fetchSummary, null, @@ -50,7 +50,7 @@ describe('Actions TestReports Store', () => { ); }); - it('should create flash on API error', done => { + it('should create flash on API error', (done) => { testAction( actions.fetchSummary, null, @@ -75,7 +75,7 @@ describe('Actions TestReports Store', () => { .replyOnce(200, testReports.test_suites[0], {}); }); - it('sets test suite and shows tests', done => { + it('sets test suite and shows tests', (done) => { const suite = testReports.test_suites[0]; const index = 0; @@ -89,7 +89,7 @@ describe('Actions TestReports Store', () => { ); }); - it('should create flash on API error', done => { + it('should create flash on API error', (done) => { const index = 0; testAction( @@ -106,7 +106,7 @@ describe('Actions TestReports Store', () => { }); describe('when we already have the suite data', () => { - it('should not fetch suite', done => { + it('should not fetch suite', (done) => { const index = 0; testReports.test_suites[0].hasFullSuite = true; @@ -116,7 +116,7 @@ describe('Actions TestReports Store', () => { }); describe('set selected suite index', () => { - it('sets selectedSuiteIndex', done => { + it('sets selectedSuiteIndex', (done) => { const selectedSuiteIndex = 0; testAction( @@ -131,7 +131,7 @@ describe('Actions TestReports Store', () => { }); describe('remove selected suite index', () => { - it('sets selectedSuiteIndex to null', done => { + it('sets selectedSuiteIndex to null', (done) => { testAction( actions.removeSelectedSuiteIndex, {}, @@ -144,11 +144,11 @@ describe('Actions TestReports Store', () => { }); describe('toggles loading', () => { - it('sets isLoading to true', done => { + it('sets isLoading to true', (done) => { testAction(actions.toggleLoading, {}, state, [{ type: types.TOGGLE_LOADING }], [], done); }); - it('toggles isLoading to false', done => { + it('toggles isLoading to false', (done) => { testAction( actions.toggleLoading, {}, diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js index 8cef499fdb9..7382a6beefa 100644 --- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js @@ -40,7 +40,7 @@ describe('Getters TestReports Store', () => { setupState(); const suites = getters.getTestSuites(state); - const expected = testReports.test_suites.map(x => ({ + const expected = testReports.test_suites.map((x) => ({ ...x, formattedTime: formattedTime(x.total_time), })); @@ -72,7 +72,7 @@ describe('Getters TestReports Store', () => { const cases = getters.getSuiteTests(state); const expected = testReports.test_suites[0].test_cases - .map(x => ({ + .map((x) => ({ ...x, formattedTime: formattedTime(x.execution_time), icon: iconForTestStatus(x.status), diff --git a/spec/frontend/pipelines/test_reports/test_case_details_spec.js b/spec/frontend/pipelines/test_reports/test_case_details_spec.js index 9e66012818e..bfb8b43778d 100644 --- a/spec/frontend/pipelines/test_reports/test_case_details_spec.js +++ b/spec/frontend/pipelines/test_reports/test_case_details_spec.js @@ -44,7 +44,7 @@ describe('Test case details', () => { }); it('renders the test case classname as modal title', () => { - expect(findModal().attributes('title')).toBe(defaultTestCase.classname); + expect(findModal().props('title')).toBe(defaultTestCase.classname); }); it('renders the test case name', () => { 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 0e00ca670a7..b8fd056610b 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -23,7 +23,7 @@ describe('Test reports suite table', () => { const noCasesMessage = () => wrapper.find('.js-no-test-cases'); const allCaseRows = () => wrapper.findAll('.js-case-row'); - const findCaseRowAtIndex = index => wrapper.findAll('.js-case-row').at(index); + const findCaseRowAtIndex = (index) => wrapper.findAll('.js-case-row').at(index); const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`); const createComponent = (suite = testSuite, perPage = 20) => { @@ -73,8 +73,8 @@ describe('Test reports suite table', () => { TestStatus.SKIPPED, TestStatus.SUCCESS, 'unknown', - ])('renders the correct icon for test case with %s status', status => { - const test = testCases.findIndex(x => x.status === status); + ])('renders the correct icon for test case with %s status', (status) => { + const test = testCases.findIndex((x) => x.status === status); const row = findCaseRowAtIndex(test); expect(findIconForRow(row, status).exists()).toBe(true); diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js index dc5af7b160c..df404d87c99 100644 --- a/spec/frontend/pipelines/test_reports/test_summary_spec.js +++ b/spec/frontend/pipelines/test_reports/test_summary_spec.js @@ -22,7 +22,7 @@ describe('Test reports summary', () => { showBack: false, }; - const createComponent = props => { + const createComponent = (props) => { wrapper = mount(Summary, { propsData: { ...defaultProps, diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js index 375325c0c6a..371ba5a4f9b 100644 --- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js @@ -26,7 +26,7 @@ describe('Pipeline Trigger Author Token', () => { }, }; - const createComponent = data => { + const createComponent = (data) => { wrapper = shallowMount(PipelineTriggerAuthorToken, { propsData: { ...defaultProps, diff --git a/spec/frontend/pipelines/unwrapping_utils_spec.js b/spec/frontend/pipelines/unwrapping_utils_spec.js index 3533599611f..cd16ed7262e 100644 --- a/spec/frontend/pipelines/unwrapping_utils_spec.js +++ b/spec/frontend/pipelines/unwrapping_utils_spec.js @@ -1,5 +1,4 @@ import { - unwrapArrayOfJobs, unwrapGroups, unwrapNodesWithName, unwrapStagesWithNeeds, @@ -89,35 +88,12 @@ const completeMock = [ { ...basicStageInfo, groups: { - nodes: groupsArray.map(group => ({ ...group, jobs: { nodes: jobArrayWithNeeds } })), + nodes: groupsArray.map((group) => ({ ...group, jobs: { nodes: jobArrayWithNeeds } })), }, }, ]; describe('Shared pipeline unwrapping utils', () => { - describe('unwrapArrayOfJobs', () => { - it('returns an empty array if the input is an empty undefined', () => { - expect(unwrapArrayOfJobs(undefined)).toEqual([]); - }); - - it('returns an empty array if the input is an empty array', () => { - expect(unwrapArrayOfJobs([])).toEqual([]); - }); - - it('returns a flatten array of each job with their data and stage name', () => { - expect( - unwrapArrayOfJobs([ - { name: 'build', groups: [{ name: 'job_a_1' }, { name: 'job_a_2' }] }, - { name: 'test', groups: [{ name: 'job_b' }] }, - ]), - ).toMatchObject([ - { category: 'build', name: 'job_a_1' }, - { category: 'build', name: 'job_a_2' }, - { category: 'test', name: 'job_b' }, - ]); - }); - }); - describe('unwrapGroups', () => { it('takes stages without nodes and returns the unwrapped groups', () => { expect(unwrapGroups(stagesAndGroups)[0].groups).toEqual(groupsArray); |