diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /spec/frontend/pipelines | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) | |
download | gitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'spec/frontend/pipelines')
21 files changed, 496 insertions, 398 deletions
diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js index 7dea6d819b9..989f6c17197 100644 --- a/spec/frontend/pipelines/components/dag/dag_spec.js +++ b/spec/frontend/pipelines/components/dag/dag_spec.js @@ -1,7 +1,4 @@ import { mount, shallowMount } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import waitForPromises from 'helpers/wait_for_promises'; import { GlAlert, GlEmptyState } from '@gitlab/ui'; import Dag from '~/pipelines/components/dag/dag.vue'; import DagGraph from '~/pipelines/components/dag/dag_graph.vue'; @@ -11,13 +8,11 @@ import { ADD_NOTE, REMOVE_NOTE, REPLACE_NOTES, - DEFAULT, PARSE_FAILURE, - LOAD_FAILURE, UNSUPPORTED_DATA, } from '~/pipelines/components/dag//constants'; import { - mockBaseData, + mockParsedGraphQLNodes, tooSmallGraph, unparseableGraph, graphWithoutDependencies, @@ -27,7 +22,6 @@ import { describe('Pipeline DAG graph wrapper', () => { let wrapper; - let mock; const getAlert = () => wrapper.find(GlAlert); const getAllAlerts = () => wrapper.findAll(GlAlert); const getGraph = () => wrapper.find(DagGraph); @@ -35,45 +29,46 @@ describe('Pipeline DAG graph wrapper', () => { const getErrorText = type => wrapper.vm.$options.errorTexts[type]; const getEmptyState = () => wrapper.find(GlEmptyState); - const dataPath = '/root/test/pipelines/90/dag.json'; - - const createComponent = (propsData = {}, method = shallowMount) => { + const createComponent = ({ + graphData = mockParsedGraphQLNodes, + provideOverride = {}, + method = shallowMount, + } = {}) => { if (wrapper?.destroy) { wrapper.destroy(); } wrapper = method(Dag, { - propsData: { + provide: { + pipelineProjectPath: 'root/abc-dag', + pipelineIid: '1', emptySvgPath: '/my-svg', dagDocPath: '/my-doc', - ...propsData, + ...provideOverride, }, data() { return { + graphData, showFailureAlert: false, }; }, }); }; - beforeEach(() => { - mock = new MockAdapter(axios); - }); - afterEach(() => { - mock.restore(); wrapper.destroy(); wrapper = null; }); - describe('when there is no dataUrl', () => { + describe('when a query argument is undefined', () => { beforeEach(() => { - createComponent({ graphUrl: undefined }); + createComponent({ + provideOverride: { pipelineProjectPath: undefined }, + graphData: null, + }); }); - it('shows the DEFAULT alert and not the graph', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe(getErrorText(DEFAULT)); + it('does not render the graph', async () => { expect(getGraph().exists()).toBe(false); }); @@ -82,36 +77,12 @@ describe('Pipeline DAG graph wrapper', () => { }); }); - describe('when there is a dataUrl', () => { - describe('but the data fetch fails', () => { + describe('when all query variables are defined', () => { + describe('but the parse fails', () => { beforeEach(async () => { - mock.onGet(dataPath).replyOnce(500); - createComponent({ graphUrl: dataPath }); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); - }); - - it('shows the LOAD_FAILURE alert and not the graph', () => { - expect(getAlert().exists()).toBe(true); - expect(getAlert().text()).toBe(getErrorText(LOAD_FAILURE)); - expect(getGraph().exists()).toBe(false); - }); - - it('does not render the empty state', () => { - expect(getEmptyState().exists()).toBe(false); - }); - }); - - describe('the data fetch succeeds but the parse fails', () => { - beforeEach(async () => { - mock.onGet(dataPath).replyOnce(200, unparseableGraph); - createComponent({ graphUrl: dataPath }); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); + createComponent({ + graphData: unparseableGraph, + }); }); it('shows the PARSE_FAILURE alert and not the graph', () => { @@ -125,19 +96,12 @@ describe('Pipeline DAG graph wrapper', () => { }); }); - describe('and the data fetch and parse succeeds', () => { + describe('parse succeeds', () => { beforeEach(async () => { - mock.onGet(dataPath).replyOnce(200, mockBaseData); - createComponent({ graphUrl: dataPath }, mount); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); + createComponent({ method: mount }); }); - it('shows the graph and the beta alert', () => { - expect(getAllAlerts().length).toBe(1); - expect(getAlert().text()).toContain('This feature is currently in beta.'); + it('shows the graph', () => { expect(getGraph().exists()).toBe(true); }); @@ -146,14 +110,11 @@ describe('Pipeline DAG graph wrapper', () => { }); }); - describe('the data fetch and parse succeeds, but the resulting graph is too small', () => { + describe('parse succeeds, but the resulting graph is too small', () => { beforeEach(async () => { - mock.onGet(dataPath).replyOnce(200, tooSmallGraph); - createComponent({ graphUrl: dataPath }); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); + createComponent({ + graphData: tooSmallGraph, + }); }); it('shows the UNSUPPORTED_DATA alert and not the graph', () => { @@ -167,19 +128,16 @@ describe('Pipeline DAG graph wrapper', () => { }); }); - describe('the data fetch succeeds but the returned data is empty', () => { + describe('the returned data is empty', () => { beforeEach(async () => { - mock.onGet(dataPath).replyOnce(200, graphWithoutDependencies); - createComponent({ graphUrl: dataPath }, mount); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); + createComponent({ + method: mount, + graphData: graphWithoutDependencies, + }); }); it('does not render an error alert or the graph', () => { - expect(getAllAlerts().length).toBe(1); - expect(getAlert().text()).toContain('This feature is currently in beta.'); + expect(getAllAlerts().length).toBe(0); expect(getGraph().exists()).toBe(false); }); @@ -191,12 +149,7 @@ describe('Pipeline DAG graph wrapper', () => { describe('annotations', () => { beforeEach(async () => { - mock.onGet(dataPath).replyOnce(200, mockBaseData); - createComponent({ graphUrl: dataPath }, mount); - - await wrapper.vm.$nextTick(); - - return waitForPromises(); + createComponent(); }); it('toggles on link mouseover and mouseout', async () => { diff --git a/spec/frontend/pipelines/components/dag/drawing_utils_spec.js b/spec/frontend/pipelines/components/dag/drawing_utils_spec.js index a50163411ed..37a7d07485b 100644 --- a/spec/frontend/pipelines/components/dag/drawing_utils_spec.js +++ b/spec/frontend/pipelines/components/dag/drawing_utils_spec.js @@ -1,9 +1,9 @@ import { createSankey } from '~/pipelines/components/dag/drawing_utils'; import { parseData } from '~/pipelines/components/dag/parsing_utils'; -import { mockBaseData } from './mock_data'; +import { mockParsedGraphQLNodes } from './mock_data'; describe('DAG visualization drawing utilities', () => { - const parsed = parseData(mockBaseData.stages); + const parsed = parseData(mockParsedGraphQLNodes); const layoutSettings = { width: 200, diff --git a/spec/frontend/pipelines/components/dag/mock_data.js b/spec/frontend/pipelines/components/dag/mock_data.js index 3b39b9cd21c..e7e93804195 100644 --- a/spec/frontend/pipelines/components/dag/mock_data.js +++ b/spec/frontend/pipelines/components/dag/mock_data.js @@ -1,127 +1,56 @@ -/* - It is important that the simple base include parallel jobs - as well as non-parallel jobs with spaces in the name to prevent - us relying on spaces as an indicator. -*/ -export const mockBaseData = { - stages: [ - { - name: 'test', - groups: [ - { - name: 'jest', - size: 2, - jobs: [{ name: 'jest 1/2', needs: ['frontend fixtures'] }, { name: 'jest 2/2' }], - }, - { - name: 'rspec', - size: 1, - jobs: [{ name: 'rspec', needs: ['frontend fixtures'] }], - }, - ], - }, - { - name: 'fixtures', - groups: [ - { - name: 'frontend fixtures', - size: 1, - jobs: [{ name: 'frontend fixtures' }], - }, - ], - }, - { - name: 'un-needed', - groups: [ - { - name: 'un-needed', - size: 1, - jobs: [{ name: 'un-needed' }], - }, - ], - }, - ], -}; - -export const tooSmallGraph = { - stages: [ - { - name: 'test', - groups: [ - { - name: 'jest', - size: 2, - jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }], - }, - { - name: 'rspec', - size: 1, - jobs: [{ name: 'rspec', needs: ['frontend fixtures'] }], - }, - ], - }, - { - name: 'fixtures', - groups: [ - { - name: 'frontend fixtures', - size: 1, - jobs: [{ name: 'frontend fixtures' }], - }, - ], - }, - { - name: 'un-needed', - groups: [ - { - name: 'un-needed', - size: 1, - jobs: [{ name: 'un-needed' }], - }, - ], - }, - ], -}; +export const tooSmallGraph = [ + { + category: 'test', + name: 'jest', + size: 2, + jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }], + }, + { + category: 'test', + name: 'rspec', + size: 1, + jobs: [{ name: 'rspec', needs: ['frontend fixtures'] }], + }, + { + category: 'fixtures', + name: 'frontend fixtures', + size: 1, + jobs: [{ name: 'frontend fixtures' }], + }, + { + category: 'un-needed', + name: 'un-needed', + size: 1, + jobs: [{ name: 'un-needed' }], + }, +]; -export const graphWithoutDependencies = { - stages: [ - { - name: 'test', - groups: [ - { - name: 'jest', - size: 2, - jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }], - }, - { - name: 'rspec', - size: 1, - jobs: [{ name: 'rspec' }], - }, - ], - }, - { - name: 'fixtures', - groups: [ - { - name: 'frontend fixtures', - size: 1, - jobs: [{ name: 'frontend fixtures' }], - }, - ], - }, - { - name: 'un-needed', - groups: [ - { - name: 'un-needed', - size: 1, - jobs: [{ name: 'un-needed' }], - }, - ], - }, - ], -}; +export const graphWithoutDependencies = [ + { + category: 'test', + name: 'jest', + size: 2, + jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }], + }, + { + category: 'test', + name: 'rspec', + size: 1, + jobs: [{ name: 'rspec' }], + }, + { + category: 'fixtures', + name: 'frontend fixtures', + size: 1, + jobs: [{ name: 'frontend fixtures' }], + }, + { + category: 'un-needed', + name: 'un-needed', + size: 1, + jobs: [{ name: 'un-needed' }], + }, +]; export const unparseableGraph = [ { @@ -468,3 +397,264 @@ export const multiNote = { }, }, }; + +/* + It is important that the base include parallel jobs + as well as non-parallel jobs with spaces in the name to prevent + us relying on spaces as an indicator. +*/ + +export const mockParsedGraphQLNodes = [ + { + category: 'build', + name: 'build_a', + size: 1, + jobs: [ + { + name: 'build_a', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'build', + name: 'build_b', + size: 1, + jobs: [ + { + name: 'build_b', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'test', + name: 'test_a', + size: 1, + jobs: [ + { + name: 'test_a', + needs: ['build_a'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'test', + name: 'test_b', + size: 1, + jobs: [ + { + name: 'test_b', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'test', + name: 'test_c', + size: 1, + jobs: [ + { + name: 'test_c', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'test', + name: 'test_d', + size: 1, + jobs: [ + { + name: 'test_d', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'post-test', + name: 'post_test_a', + size: 1, + jobs: [ + { + name: 'post_test_a', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'post-test', + name: 'post_test_b', + size: 1, + jobs: [ + { + name: 'post_test_b', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'post-test', + name: 'post_test_c', + size: 1, + jobs: [ + { + name: 'post_test_c', + needs: ['test_b', 'test_a'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'staging', + name: 'staging_a', + size: 1, + jobs: [ + { + name: 'staging_a', + needs: ['post_test_a'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'staging', + name: 'staging_b', + size: 1, + jobs: [ + { + name: 'staging_b', + needs: ['post_test_b'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'staging', + name: 'staging_c', + size: 1, + jobs: [ + { + name: 'staging_c', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'staging', + name: 'staging_d', + size: 1, + jobs: [ + { + name: 'staging_d', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'staging', + name: 'staging_e', + size: 1, + jobs: [ + { + name: 'staging_e', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'canary', + name: 'canary_a', + size: 1, + jobs: [ + { + name: 'canary_a', + needs: ['staging_b', 'staging_a'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'canary', + name: 'canary_b', + size: 1, + jobs: [ + { + name: 'canary_b', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'canary', + name: 'canary_c', + size: 1, + jobs: [ + { + name: 'canary_c', + needs: ['staging_b'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'production', + name: 'production_a', + size: 1, + jobs: [ + { + name: 'production_a', + needs: ['canary_a'], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'production', + name: 'production_b', + size: 1, + jobs: [ + { + name: 'production_b', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'production', + name: 'production_c', + size: 1, + jobs: [ + { + name: 'production_c', + needs: [], + }, + ], + __typename: 'CiGroup', + }, + { + category: 'production', + name: 'production_d', + size: 1, + jobs: [ + { + name: 'production_d', + needs: ['canary_c'], + }, + ], + __typename: 'CiGroup', + }, +]; diff --git a/spec/frontend/pipelines/components/dag/parsing_utils_spec.js b/spec/frontend/pipelines/components/dag/parsing_utils_spec.js index d9a1296e572..e93fa8e6760 100644 --- a/spec/frontend/pipelines/components/dag/parsing_utils_spec.js +++ b/spec/frontend/pipelines/components/dag/parsing_utils_spec.js @@ -1,5 +1,5 @@ import { - createNodesStructure, + createNodeDict, makeLinksFromNodes, filterByAncestors, parseData, @@ -8,56 +8,17 @@ import { } from '~/pipelines/components/dag/parsing_utils'; import { createSankey } from '~/pipelines/components/dag/drawing_utils'; -import { mockBaseData } from './mock_data'; +import { mockParsedGraphQLNodes } from './mock_data'; describe('DAG visualization parsing utilities', () => { - const { nodes, nodeDict } = createNodesStructure(mockBaseData.stages); - const unfilteredLinks = makeLinksFromNodes(nodes, nodeDict); - const parsed = parseData(mockBaseData.stages); - - const layoutSettings = { - width: 200, - height: 200, - nodeWidth: 10, - nodePadding: 20, - paddingForLabels: 100, - }; - - const sankeyLayout = createSankey(layoutSettings)(parsed); - - describe('createNodesStructure', () => { - const parallelGroupName = 'jest'; - const parallelJobName = 'jest 1/2'; - const singleJobName = 'frontend fixtures'; - - const { name, jobs, size } = mockBaseData.stages[0].groups[0]; - - it('returns the expected node structure', () => { - expect(nodes[0]).toHaveProperty('category', mockBaseData.stages[0].name); - expect(nodes[0]).toHaveProperty('name', name); - expect(nodes[0]).toHaveProperty('jobs', jobs); - expect(nodes[0]).toHaveProperty('size', size); - }); - - it('adds needs to top level of nodeDict entries', () => { - expect(nodeDict[parallelGroupName]).toHaveProperty('needs'); - expect(nodeDict[parallelJobName]).toHaveProperty('needs'); - expect(nodeDict[singleJobName]).toHaveProperty('needs'); - }); - - it('makes entries in nodeDict for jobs and parallel jobs', () => { - const nodeNames = Object.keys(nodeDict); - - expect(nodeNames.includes(parallelGroupName)).toBe(true); - expect(nodeNames.includes(parallelJobName)).toBe(true); - expect(nodeNames.includes(singleJobName)).toBe(true); - }); - }); + const nodeDict = createNodeDict(mockParsedGraphQLNodes); + const unfilteredLinks = makeLinksFromNodes(mockParsedGraphQLNodes, nodeDict); + const parsed = parseData(mockParsedGraphQLNodes); describe('makeLinksFromNodes', () => { it('returns the expected link structure', () => { - expect(unfilteredLinks[0]).toHaveProperty('source', 'frontend fixtures'); - expect(unfilteredLinks[0]).toHaveProperty('target', 'jest'); + expect(unfilteredLinks[0]).toHaveProperty('source', 'build_a'); + expect(unfilteredLinks[0]).toHaveProperty('target', 'test_a'); expect(unfilteredLinks[0]).toHaveProperty('value', 10); }); }); @@ -107,8 +68,22 @@ describe('DAG visualization parsing utilities', () => { describe('removeOrphanNodes', () => { it('removes sankey nodes that have no needs and are not needed', () => { + const layoutSettings = { + width: 200, + height: 200, + nodeWidth: 10, + nodePadding: 20, + paddingForLabels: 100, + }; + + const sankeyLayout = createSankey(layoutSettings)(parsed); const cleanedNodes = removeOrphanNodes(sankeyLayout.nodes); - expect(cleanedNodes).toHaveLength(sankeyLayout.nodes.length - 1); + /* + These lengths are determined by the mock data. + If the data changes, the numbers may also change. + */ + expect(parsed.nodes).toHaveLength(21); + expect(cleanedNodes).toHaveLength(12); }); }); diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js index add7b56845e..c5b7318d3af 100644 --- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js +++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js @@ -1,10 +1,10 @@ -import Api from '~/api'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; +import { GlFilteredSearch } from '@gitlab/ui'; +import Api from '~/api'; import axios from '~/lib/utils/axios_utils'; import PipelinesFilteredSearch from '~/pipelines/components/pipelines_list/pipelines_filtered_search.vue'; import { users, mockSearch, branches, tags } from '../mock_data'; -import { GlFilteredSearch } from '@gitlab/ui'; describe('Pipelines filtered search', () => { let wrapper; diff --git a/spec/frontend/pipelines/graph/action_component_spec.js b/spec/frontend/pipelines/graph/action_component_spec.js index 3c5938cfa1f..ab477292bc1 100644 --- a/spec/frontend/pipelines/graph/action_component_spec.js +++ b/spec/frontend/pipelines/graph/action_component_spec.js @@ -1,4 +1,5 @@ import { mount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; @@ -7,7 +8,7 @@ import ActionComponent from '~/pipelines/components/graph/action_component.vue'; describe('pipeline graph action component', () => { let wrapper; let mock; - const findButton = () => wrapper.find('button'); + const findButton = () => wrapper.find(GlButton); beforeEach(() => { mock = new MockAdapter(axios); diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js index 9731ce3f8a6..1389649abea 100644 --- a/spec/frontend/pipelines/graph/graph_component_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import { mount } from '@vue/test-utils'; +import { setHTMLFixture } from 'helpers/fixtures'; import PipelineStore from '~/pipelines/stores/pipeline_store'; import graphComponent from '~/pipelines/components/graph/graph_component.vue'; import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; @@ -7,7 +8,6 @@ import linkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines import graphJSON from './mock_data'; import linkedPipelineJSON from './linked_pipelines_mock_data'; import PipelinesMediator from '~/pipelines/pipeline_details_mediator'; -import { setHTMLFixture } from 'helpers/fixtures'; describe('graph component', () => { const store = new PipelineStore(); diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js index 133d5695afb..59121c54ff3 100644 --- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js @@ -1,4 +1,5 @@ import { mount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue'; import CiStatus from '~/vue_shared/components/ci_icon.vue'; @@ -12,7 +13,7 @@ const invalidTriggeredPipelineId = mockPipeline.project.id + 5; describe('Linked pipeline', () => { let wrapper; - const findButton = () => wrapper.find('button'); + const findButton = () => wrapper.find(GlButton); const findPipelineLabel = () => wrapper.find('[data-testid="downstream-pipeline-label"]'); const findLinkedPipeline = () => wrapper.find({ ref: 'linkedPipeline' }); @@ -42,9 +43,7 @@ describe('Linked pipeline', () => { }); it('should render a button', () => { - const linkElement = wrapper.find('.js-linked-pipeline-content'); - - expect(linkElement.exists()).toBe(true); + expect(findButton().exists()).toBe(true); }); it('should render the project name', () => { @@ -62,7 +61,7 @@ describe('Linked pipeline', () => { }); it('should have a ci-status child component', () => { - expect(wrapper.find('.js-linked-pipeline-status').exists()).toBe(true); + expect(wrapper.find(CiStatus).exists()).toBe(true); }); it('should render the pipeline id', () => { @@ -77,15 +76,14 @@ describe('Linked pipeline', () => { }); it('should render the tooltip text as the title attribute', () => { - const tooltipRef = wrapper.find('.js-linked-pipeline-content'); - const titleAttr = tooltipRef.attributes('title'); + const titleAttr = findButton().attributes('title'); expect(titleAttr).toContain(mockPipeline.project.name); expect(titleAttr).toContain(mockPipeline.details.status.label); }); - it('does not render the loading icon when isLoading is false', () => { - expect(wrapper.find('.js-linked-pipeline-loading').exists()).toBe(false); + it('sets the loading prop to false', () => { + expect(findButton().props('loading')).toBe(false); }); it('should display multi-project label when pipeline project id is not the same as triggered pipeline project id', () => { @@ -132,8 +130,8 @@ describe('Linked pipeline', () => { createWrapper(props); }); - it('renders a loading icon', () => { - expect(wrapper.find('.js-linked-pipeline-loading').exists()).toBe(true); + it('sets the loading prop to true', () => { + expect(findButton().props('loading')).toBe(true); }); }); diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js index 1c3a6c545a0..5388d624d3c 100644 --- a/spec/frontend/pipelines/header_component_spec.js +++ b/spec/frontend/pipelines/header_component_spec.js @@ -1,8 +1,8 @@ import { shallowMount } from '@vue/test-utils'; +import { GlModal } from '@gitlab/ui'; import HeaderComponent from '~/pipelines/components/header_component.vue'; import CiHeader from '~/vue_shared/components/header_ci_component.vue'; import eventHub from '~/pipelines/event_hub'; -import { GlModal } from '@gitlab/ui'; describe('Pipeline details header', () => { let wrapper; @@ -85,13 +85,13 @@ describe('Pipeline details header', () => { }); it('should call postAction when retry button action is clicked', () => { - wrapper.find('.js-retry-button').vm.$emit('click'); + wrapper.find('[data-testid="retryButton"]').vm.$emit('click'); expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'retry'); }); it('should call postAction when cancel button action is clicked', () => { - wrapper.find('.js-btn-cancel-pipeline').vm.$emit('click'); + wrapper.find('[data-testid="cancelPipeline"]').vm.$emit('click'); expect(eventHub.$emit).toHaveBeenCalledWith('headerPostAction', 'cancel'); }); diff --git a/spec/frontend/pipelines/pipeline_details_mediator_spec.js b/spec/frontend/pipelines/pipeline_details_mediator_spec.js index 083e97666ed..d6699a43b54 100644 --- a/spec/frontend/pipelines/pipeline_details_mediator_spec.js +++ b/spec/frontend/pipelines/pipeline_details_mediator_spec.js @@ -1,7 +1,7 @@ import MockAdapter from 'axios-mock-adapter'; +import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import PipelineMediator from '~/pipelines/pipeline_details_mediator'; -import waitForPromises from 'helpers/wait_for_promises'; describe('PipelineMdediator', () => { let mediator; diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js index aef54d94974..cce4c2dfa7b 100644 --- a/spec/frontend/pipelines/pipelines_actions_spec.js +++ b/spec/frontend/pipelines/pipelines_actions_spec.js @@ -1,11 +1,11 @@ import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import { TEST_HOST } from 'spec/test_constants'; +import { GlDeprecatedButton } from '@gitlab/ui'; +import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import PipelinesActions from '~/pipelines/components/pipelines_list/pipelines_actions.vue'; -import { GlDeprecatedButton } from '@gitlab/ui'; import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import waitForPromises from 'helpers/wait_for_promises'; describe('Pipelines Actions dropdown', () => { let wrapper; diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js index 512205c3fc3..83f6cb68eba 100644 --- a/spec/frontend/pipelines/pipelines_artifacts_spec.js +++ b/spec/frontend/pipelines/pipelines_artifacts_spec.js @@ -1,6 +1,6 @@ import { shallowMount } from '@vue/test-utils'; -import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; import { GlLink } from '@gitlab/ui'; +import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; describe('Pipelines Artifacts dropdown', () => { let wrapper; diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js index 66446b9aa1d..b0ad6bbd228 100644 --- a/spec/frontend/pipelines/pipelines_spec.js +++ b/spec/frontend/pipelines/pipelines_spec.js @@ -1,16 +1,16 @@ -import Api from '~/api'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; import waitForPromises from 'helpers/wait_for_promises'; +import { GlFilteredSearch } from '@gitlab/ui'; +import Api from '~/api'; +import axios from '~/lib/utils/axios_utils'; import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue'; import Store from '~/pipelines/stores/pipelines_store'; import { pipelineWithStages, stageReply, users, mockSearch, branches } from './mock_data'; import { RAW_TEXT_WARNING } from '~/pipelines/constants'; -import { GlFilteredSearch } from '@gitlab/ui'; -import createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; -jest.mock('~/flash', () => jest.fn()); +jest.mock('~/flash'); describe('Pipelines', () => { const jsonFixtureName = 'pipelines/pipelines.json'; diff --git a/spec/frontend/pipelines/stage_spec.js b/spec/frontend/pipelines/stage_spec.js index 547f8994ca5..e134b81856b 100644 --- a/spec/frontend/pipelines/stage_spec.js +++ b/spec/frontend/pipelines/stage_spec.js @@ -1,10 +1,10 @@ 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'; import { stageReply } from './mock_data'; -import waitForPromises from 'helpers/wait_for_promises'; describe('Pipelines stage component', () => { let wrapper; diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js index d4647c55a53..1809f15a6e6 100644 --- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js @@ -5,7 +5,7 @@ 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 createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; jest.mock('~/flash.js'); @@ -16,14 +16,13 @@ describe('Actions TestReports Store', () => { const testReports = getJSONFixture('pipelines/test_report.json'); const summary = { total_count: 1 }; - const fullReportEndpoint = `${TEST_HOST}/test_reports.json`; + const suiteEndpoint = `${TEST_HOST}/tests/:suite_name.json`; const summaryEndpoint = `${TEST_HOST}/test_reports/summary.json`; const defaultState = { - fullReportEndpoint, + suiteEndpoint, summaryEndpoint, testReports: {}, selectedSuite: null, - useBuildSummaryReport: false, }; beforeEach(() => { @@ -40,89 +39,63 @@ describe('Actions TestReports Store', () => { mock.onGet(summaryEndpoint).replyOnce(200, summary, {}); }); - describe('when useBuildSummaryReport in state is true', () => { - it('sets testReports and shows tests', done => { - testAction( - actions.fetchSummary, - null, - { ...state, useBuildSummaryReport: true }, - [{ type: types.SET_SUMMARY, payload: summary }], - [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], - done, - ); - }); - - it('should create flash on API error', done => { - testAction( - actions.fetchSummary, - null, - { - summaryEndpoint: null, - useBuildSummaryReport: true, - }, - [], - [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, - ); - }); + it('sets testReports and shows tests', done => { + testAction( + actions.fetchSummary, + null, + state, + [{ type: types.SET_SUMMARY, payload: summary }], + [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], + done, + ); }); - describe('when useBuildSummaryReport in state is false', () => { - it('sets testReports and shows tests', done => { - testAction( - actions.fetchSummary, - null, - state, - [{ type: types.SET_SUMMARY, payload: summary }], - [], - done, - ); - }); - - it('should create flash on API error', done => { - testAction( - actions.fetchSummary, - null, - { - summaryEndpoint: null, - }, - [], - [], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, - ); - }); + it('should create flash on API error', done => { + testAction( + actions.fetchSummary, + null, + { summaryEndpoint: null }, + [], + [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], + () => { + expect(createFlash).toHaveBeenCalled(); + done(); + }, + ); }); }); - describe('fetch full report', () => { + describe('fetch test suite', () => { beforeEach(() => { - mock.onGet(fullReportEndpoint).replyOnce(200, testReports, {}); + const buildIds = [1]; + testReports.test_suites[0].build_ids = buildIds; + const endpoint = suiteEndpoint.replace(':suite_name', testReports.test_suites[0].name); + mock + .onGet(endpoint, { params: { build_ids: buildIds } }) + .replyOnce(200, testReports.test_suites[0], {}); }); - it('sets testReports and shows tests', done => { + it('sets test suite and shows tests', done => { + const suite = testReports.test_suites[0]; + const index = 0; + testAction( - actions.fetchFullReport, - null, - state, - [{ type: types.SET_REPORTS, payload: testReports }], + actions.fetchTestSuite, + index, + { ...state, testReports }, + [{ type: types.SET_SUITE, payload: { suite, index } }], [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], done, ); }); it('should create flash on API error', done => { + const index = 0; + testAction( - actions.fetchFullReport, - null, - { - fullReportEndpoint: null, - }, + actions.fetchTestSuite, + index, + { ...state, testReports, suiteEndpoint: null }, [], [{ type: 'toggleLoading' }, { type: 'toggleLoading' }], () => { @@ -131,6 +104,15 @@ describe('Actions TestReports Store', () => { }, ); }); + + describe('when we already have the suite data', () => { + it('should not fetch suite', done => { + const index = 0; + testReports.test_suites[0].hasFullSuite = true; + + testAction(actions.fetchTestSuite, index, { ...state, testReports }, [], [], done); + }); + }); }); describe('set selected suite index', () => { diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js index f4cc5c4bc5d..b935029bc6a 100644 --- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js @@ -12,20 +12,24 @@ describe('Mutations TestReports Store', () => { testReports: {}, selectedSuite: null, isLoading: false, - hasFullReport: false, }; beforeEach(() => { mockState = { ...defaultState }; }); - describe('set reports', () => { - it('should set testReports', () => { - const expectedState = { ...mockState, testReports }; - mutations[types.SET_REPORTS](mockState, testReports); + describe('set suite', () => { + it('should set the suite at the given index', () => { + mockState.testReports = testReports; + const suite = { name: 'test_suite' }; + const index = 0; + const expectedState = { ...mockState }; + expectedState.testReports.test_suites[index] = { suite, hasFullSuite: true }; + mutations[types.SET_SUITE](mockState, { suite, index }); - expect(mockState.testReports).toEqual(expectedState.testReports); - expect(mockState.hasFullReport).toBe(true); + expect(mockState.testReports.test_suites[index]).toEqual( + expectedState.testReports.test_suites[index], + ); }); }); @@ -40,10 +44,21 @@ describe('Mutations TestReports Store', () => { describe('set summary', () => { it('should set summary', () => { - const summary = { total_count: 1 }; + const summary = { + total: { time: 0, count: 10, success: 1, failed: 2, skipped: 3, error: 4 }, + }; + const expectedSummary = { + ...summary, + total_time: 0, + total_count: 10, + success_count: 1, + failed_count: 2, + skipped_count: 3, + error_count: 4, + }; mutations[types.SET_SUMMARY](mockState, summary); - expect(mockState.testReports).toEqual(summary); + expect(mockState.testReports).toEqual(expectedSummary); }); }); diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js index ef0bcffabe3..a709edf5184 100644 --- a/spec/frontend/pipelines/test_reports/test_reports_spec.js +++ b/spec/frontend/pipelines/test_reports/test_reports_spec.js @@ -22,7 +22,7 @@ describe('Test reports app', () => { const testSummaryTable = () => wrapper.find(TestSummaryTable); const actionSpies = { - fetchFullReport: jest.fn(), + fetchTestSuite: jest.fn(), fetchSummary: jest.fn(), setSelectedSuiteIndex: jest.fn(), removeSelectedSuiteIndex: jest.fn(), @@ -91,28 +91,14 @@ describe('Test reports app', () => { }); describe('when a suite is clicked', () => { - describe('when the full test report has already been received', () => { - beforeEach(() => { - createComponent({ hasFullReport: true }); - testSummaryTable().vm.$emit('row-click', 0); - }); - - it('should only call setSelectedSuiteIndex', () => { - expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled(); - expect(actionSpies.fetchFullReport).not.toHaveBeenCalled(); - }); + beforeEach(() => { + createComponent({ hasFullReport: true }); + testSummaryTable().vm.$emit('row-click', 0); }); - describe('when the full test report has not been received', () => { - beforeEach(() => { - createComponent({ hasFullReport: false }); - testSummaryTable().vm.$emit('row-click', 0); - }); - - it('should call setSelectedSuiteIndex and fetchFullReport', () => { - expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled(); - expect(actionSpies.fetchFullReport).toHaveBeenCalled(); - }); + it('should call setSelectedSuiteIndex and fetchTestSuite', () => { + expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled(); + expect(actionSpies.fetchTestSuite).toHaveBeenCalled(); }); }); 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 65bffe7039a..3a4aa94571e 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -23,6 +23,8 @@ 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 allCaseNames = () => + wrapper.findAll('[data-testid="caseName"]').wrappers.map(el => el.attributes('text')); const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`); const createComponent = (suite = testSuite) => { @@ -61,18 +63,14 @@ describe('Test reports suite table', () => { expect(allCaseRows().length).toBe(testCases.length); }); - it('renders the failed tests first', () => { - const failedCaseNames = testCases - .filter(x => x.status === TestStatus.FAILED) - .map(x => x.name); + it('renders the failed tests first, skipped tests next, then successful tests', () => { + const expectedCaseOrder = [ + ...testCases.filter(x => x.status === TestStatus.FAILED), + ...testCases.filter(x => x.status === TestStatus.SKIPPED), + ...testCases.filter(x => x.status === TestStatus.SUCCESS), + ].map(x => x.name); - const skippedCaseNames = testCases - .filter(x => x.status === TestStatus.SKIPPED) - .map(x => x.name); - - expect(findCaseRowAtIndex(0).text()).toContain(failedCaseNames[0]); - expect(findCaseRowAtIndex(1).text()).toContain(failedCaseNames[1]); - expect(findCaseRowAtIndex(2).text()).toContain(skippedCaseNames[0]); + expect(allCaseNames()).toEqual(expectedCaseOrder); }); it('renders the correct icon for each status', () => { diff --git a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js index 650dd8a1def..2e32d62b4bd 100644 --- a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js @@ -1,6 +1,6 @@ -import Api from '~/api'; import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import Api from '~/api'; import PipelineBranchNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue'; import { branches, mockBranchesAfterMap } from '../mock_data'; diff --git a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js index 15b283dc2ff..42c9dfc9ff0 100644 --- a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js @@ -1,6 +1,6 @@ -import Api from '~/api'; import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import Api from '~/api'; import PipelineTagNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue'; import { tags, mockTagsAfterMap } from '../mock_data'; 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 0b5cf2e202b..c95d2ea1b7b 100644 --- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js +++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js @@ -1,6 +1,6 @@ -import Api from '~/api'; import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import Api from '~/api'; import PipelineTriggerAuthorToken from '~/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue'; import { users } from '../mock_data'; |