Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-19 04:45:44 +0300
commit85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch)
tree9160f299afd8c80c038f08e1545be119f5e3f1e1 /spec/frontend/pipelines
parent15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff)
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'spec/frontend/pipelines')
-rw-r--r--spec/frontend/pipelines/components/pipelines_filtered_search_spec.js3
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js7
-rw-r--r--spec/frontend/pipelines/graph/job_item_spec.js68
-rw-r--r--spec/frontend/pipelines/graph/linked_pipeline_spec.js75
-rw-r--r--spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js47
-rw-r--r--spec/frontend/pipelines/pipeline_graph/mock_data.js80
-rw-r--r--spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js59
-rw-r--r--spec/frontend/pipelines/pipeline_graph/utils_spec.js150
-rw-r--r--spec/frontend/pipelines/pipeline_triggerer_spec.js2
-rw-r--r--spec/frontend/pipelines/pipelines_actions_spec.js6
-rw-r--r--spec/frontend/pipelines/test_reports/stores/getters_spec.js6
-rw-r--r--spec/frontend/pipelines/test_reports/stores/utils_spec.js26
-rw-r--r--spec/frontend/pipelines/test_reports/test_reports_spec.js11
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js12
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_spec.js3
-rw-r--r--spec/frontend/pipelines/time_ago_spec.js23
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js17
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js32
18 files changed, 539 insertions, 88 deletions
diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
index c5b7318d3af..8a6586a7d7d 100644
--- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
@@ -47,9 +47,6 @@ describe('Pipelines filtered search', () => {
});
it('displays UI elements', () => {
- expect(wrapper.isVueInstance()).toBe(true);
- expect(wrapper.isEmpty()).toBe(false);
-
expect(findFilteredSearch().exists()).toBe(true);
});
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
index 1389649abea..d977db58a0e 100644
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_spec.js
@@ -16,6 +16,9 @@ describe('graph component', () => {
let wrapper;
+ const findExpandPipelineBtn = () => wrapper.find('[data-testid="expandPipelineButton"]');
+ const findAllExpandPipelineBtns = () => wrapper.findAll('[data-testid="expandPipelineButton"]');
+
beforeEach(() => {
setHTMLFixture('<div class="layout-page"></div>');
});
@@ -167,7 +170,7 @@ describe('graph component', () => {
describe('triggered by', () => {
describe('on click', () => {
it('should emit `onClickTriggeredBy` when triggered by linked pipeline is clicked', () => {
- const btnWrapper = wrapper.find('.linked-pipeline-content');
+ const btnWrapper = findExpandPipelineBtn();
btnWrapper.trigger('click');
@@ -213,7 +216,7 @@ describe('graph component', () => {
),
});
- const btnWrappers = wrapper.findAll('.linked-pipeline-content');
+ const btnWrappers = findAllExpandPipelineBtns();
const downstreamBtnWrapper = btnWrappers.at(btnWrappers.length - 1);
downstreamBtnWrapper.trigger('click');
diff --git a/spec/frontend/pipelines/graph/job_item_spec.js b/spec/frontend/pipelines/graph/job_item_spec.js
index 2c5e7a1f6e9..e844cbc5bf8 100644
--- a/spec/frontend/pipelines/graph/job_item_spec.js
+++ b/spec/frontend/pipelines/graph/job_item_spec.js
@@ -6,6 +6,7 @@ describe('pipeline graph job item', () => {
let wrapper;
const findJobWithoutLink = () => wrapper.find('[data-testid="job-without-link"]');
+ const findJobWithLink = () => wrapper.find('[data-testid="job-with-link"]');
const createWrapper = propsData => {
wrapper = mount(JobItem, {
@@ -13,6 +14,7 @@ describe('pipeline graph job item', () => {
});
};
+ const triggerActiveClass = 'gl-shadow-x0-y0-b3-s1-blue-500';
const delayedJobFixture = getJSONFixture('jobs/delayed.json');
const mockJob = {
id: 4256,
@@ -33,6 +35,18 @@ describe('pipeline graph job item', () => {
},
},
};
+ const mockJobWithoutDetails = {
+ id: 4257,
+ name: 'job_without_details',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ details_path: '/root/ci-mock/builds/4257',
+ has_details: false,
+ },
+ };
afterEach(() => {
wrapper.destroy();
@@ -47,7 +61,7 @@ describe('pipeline graph job item', () => {
expect(link.attributes('href')).toBe(mockJob.status.details_path);
- expect(link.attributes('title')).toEqual(`${mockJob.name} - ${mockJob.status.label}`);
+ expect(link.attributes('title')).toBe(`${mockJob.name} - ${mockJob.status.label}`);
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
@@ -61,18 +75,7 @@ describe('pipeline graph job item', () => {
describe('name without link', () => {
beforeEach(() => {
createWrapper({
- job: {
- id: 4257,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4257',
- has_details: false,
- },
- },
+ job: mockJobWithoutDetails,
cssClassJobName: 'css-class-job-name',
jobHovered: 'test',
});
@@ -82,11 +85,10 @@ describe('pipeline graph job item', () => {
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
expect(wrapper.find('a').exists()).toBe(false);
- expect(trimText(wrapper.find('.ci-status-text').text())).toEqual(mockJob.name);
+ expect(trimText(wrapper.find('.ci-status-text').text())).toBe(mockJobWithoutDetails.name);
});
it('should apply hover class and provided class name', () => {
- expect(findJobWithoutLink().classes()).toContain('gl-inset-border-1-blue-500');
expect(findJobWithoutLink().classes()).toContain('css-class-job-name');
});
});
@@ -137,9 +139,7 @@ describe('pipeline graph job item', () => {
},
});
- expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toEqual(
- 'test - success',
- );
+ expect(wrapper.find('.js-job-component-tooltip').attributes('title')).toBe('test - success');
});
});
@@ -149,9 +149,39 @@ describe('pipeline graph job item', () => {
job: delayedJobFixture,
});
- expect(wrapper.find('.js-pipeline-graph-job-link').attributes('title')).toEqual(
+ expect(findJobWithLink().attributes('title')).toBe(
`delayed job - delayed manual action (${wrapper.vm.remainingTime})`,
);
});
});
+
+ describe('trigger job highlighting', () => {
+ it.each`
+ job | jobName | expanded | link
+ ${mockJob} | ${mockJob.name} | ${true} | ${true}
+ ${mockJobWithoutDetails} | ${mockJobWithoutDetails.name} | ${true} | ${false}
+ `(
+ `trigger job should stay highlighted when downstream is expanded`,
+ ({ job, jobName, expanded, link }) => {
+ createWrapper({ job, pipelineExpanded: { jobName, expanded } });
+ const findJobEl = link ? findJobWithLink : findJobWithoutLink;
+
+ expect(findJobEl().classes()).toContain(triggerActiveClass);
+ },
+ );
+
+ it.each`
+ job | jobName | expanded | link
+ ${mockJob} | ${mockJob.name} | ${false} | ${true}
+ ${mockJobWithoutDetails} | ${mockJobWithoutDetails.name} | ${false} | ${false}
+ `(
+ `trigger job should not be highlighted when downstream is not expanded`,
+ ({ job, jobName, expanded, link }) => {
+ createWrapper({ job, pipelineExpanded: { jobName, expanded } });
+ const findJobEl = link ? findJobWithLink : findJobWithoutLink;
+
+ expect(findJobEl().classes()).not.toContain(triggerActiveClass);
+ },
+ );
+ });
});
diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
index 59121c54ff3..8e65f0d4f71 100644
--- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import { GlButton } from '@gitlab/ui';
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue';
import CiStatus from '~/vue_shared/components/ci_icon.vue';
@@ -16,10 +16,18 @@ describe('Linked pipeline', () => {
const findButton = () => wrapper.find(GlButton);
const findPipelineLabel = () => wrapper.find('[data-testid="downstream-pipeline-label"]');
const findLinkedPipeline = () => wrapper.find({ ref: 'linkedPipeline' });
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findPipelineLink = () => wrapper.find('[data-testid="pipelineLink"]');
+ const findExpandButton = () => wrapper.find('[data-testid="expandPipelineButton"]');
- const createWrapper = propsData => {
+ const createWrapper = (propsData, data = []) => {
wrapper = mount(LinkedPipelineComponent, {
propsData,
+ data() {
+ return {
+ ...data,
+ };
+ },
});
};
@@ -39,7 +47,7 @@ describe('Linked pipeline', () => {
});
it('should render a list item as the containing element', () => {
- expect(wrapper.is('li')).toBe(true);
+ expect(wrapper.element.tagName).toBe('LI');
});
it('should render a button', () => {
@@ -76,7 +84,7 @@ describe('Linked pipeline', () => {
});
it('should render the tooltip text as the title attribute', () => {
- const titleAttr = findButton().attributes('title');
+ const titleAttr = findLinkedPipeline().attributes('title');
expect(titleAttr).toContain(mockPipeline.project.name);
expect(titleAttr).toContain(mockPipeline.details.status.label);
@@ -117,6 +125,56 @@ describe('Linked pipeline', () => {
createWrapper(upstreamProps);
expect(findPipelineLabel().exists()).toBe(true);
});
+
+ it('downstream pipeline should contain the correct link', () => {
+ createWrapper(downstreamProps);
+ expect(findPipelineLink().attributes('href')).toBe(mockData.triggered_by.path);
+ });
+
+ it('upstream pipeline should contain the correct link', () => {
+ createWrapper(upstreamProps);
+ expect(findPipelineLink().attributes('href')).toBe(mockData.triggered_by.path);
+ });
+
+ it.each`
+ presentClass | missingClass
+ ${'gl-right-0'} | ${'gl-left-0'}
+ ${'gl-border-l-1!'} | ${'gl-border-r-1!'}
+ `(
+ 'pipeline expand button should be postioned right when child pipeline',
+ ({ presentClass, missingClass }) => {
+ createWrapper(downstreamProps);
+ expect(findExpandButton().classes()).toContain(presentClass);
+ expect(findExpandButton().classes()).not.toContain(missingClass);
+ },
+ );
+
+ it.each`
+ presentClass | missingClass
+ ${'gl-left-0'} | ${'gl-right-0'}
+ ${'gl-border-r-1!'} | ${'gl-border-l-1!'}
+ `(
+ 'pipeline expand button should be postioned left when parent pipeline',
+ ({ presentClass, missingClass }) => {
+ createWrapper(upstreamProps);
+ expect(findExpandButton().classes()).toContain(presentClass);
+ expect(findExpandButton().classes()).not.toContain(missingClass);
+ },
+ );
+
+ it.each`
+ pipelineType | anglePosition | expanded
+ ${downstreamProps} | ${'angle-right'} | ${false}
+ ${downstreamProps} | ${'angle-left'} | ${true}
+ ${upstreamProps} | ${'angle-left'} | ${false}
+ ${upstreamProps} | ${'angle-right'} | ${true}
+ `(
+ '$pipelineType.columnTitle pipeline button icon should be $anglePosition if expanded state is $expanded',
+ ({ pipelineType, anglePosition, expanded }) => {
+ createWrapper(pipelineType, { expanded });
+ expect(findExpandButton().props('icon')).toBe(anglePosition);
+ },
+ );
});
describe('when isLoading is true', () => {
@@ -130,8 +188,8 @@ describe('Linked pipeline', () => {
createWrapper(props);
});
- it('sets the loading prop to true', () => {
- expect(findButton().props('loading')).toBe(true);
+ it('loading icon is visible', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
});
});
@@ -172,5 +230,10 @@ describe('Linked pipeline', () => {
findLinkedPipeline().trigger('mouseleave');
expect(wrapper.emitted().downstreamHovered).toStrictEqual([['']]);
});
+
+ it('should emit pipelineExpanded with job name and expanded state on click', () => {
+ findExpandButton().trigger('click');
+ expect(wrapper.emitted().pipelineExpandToggle).toStrictEqual([['trigger_job', true]]);
+ });
});
});
diff --git a/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js b/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js
new file mode 100644
index 00000000000..fea42350959
--- /dev/null
+++ b/spec/frontend/pipelines/pipeline_graph/gitlab_ci_yaml_visualization_spec.js
@@ -0,0 +1,47 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlTab } from '@gitlab/ui';
+import { yamlString } from './mock_data';
+import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
+import GitlabCiYamlVisualization from '~/pipelines/components/pipeline_graph/gitlab_ci_yaml_visualization.vue';
+
+describe('gitlab yaml visualization component', () => {
+ const defaultProps = { blobData: yamlString };
+ let wrapper;
+
+ const createComponent = props => {
+ return shallowMount(GitlabCiYamlVisualization, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ const findGlTabComponents = () => wrapper.findAll(GlTab);
+ const findPipelineGraph = () => wrapper.find(PipelineGraph);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('tabs component', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('renders the file and visualization tabs', () => {
+ expect(findGlTabComponents()).toHaveLength(2);
+ });
+ });
+
+ describe('graph component', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+
+ it('is hidden by default', () => {
+ expect(findPipelineGraph().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/pipeline_graph/mock_data.js b/spec/frontend/pipelines/pipeline_graph/mock_data.js
new file mode 100644
index 00000000000..5a5d6c021a6
--- /dev/null
+++ b/spec/frontend/pipelines/pipeline_graph/mock_data.js
@@ -0,0 +1,80 @@
+export const yamlString = `stages:
+- empty
+- build
+- test
+- deploy
+- final
+
+include:
+- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
+
+build_a:
+ stage: build
+ script: echo hello
+build_b:
+ stage: build
+ script: echo hello
+build_c:
+ stage: build
+ script: echo hello
+build_d:
+ stage: Queen
+ script: echo hello
+
+test_a:
+ stage: test
+ script: ls
+ needs: [build_a, build_b, build_c]
+test_b:
+ stage: test
+ script: ls
+ needs: [build_a, build_b, build_d]
+test_c:
+ stage: test
+ script: ls
+ needs: [build_a, build_b, build_c]
+
+deploy_a:
+ stage: deploy
+ script: echo hello
+`;
+
+export const pipelineData = {
+ stages: [
+ {
+ name: 'build',
+ groups: [],
+ },
+ {
+ name: 'build',
+ groups: [
+ {
+ name: 'build_1',
+ jobs: [{ script: 'echo hello', stage: 'build' }],
+ },
+ ],
+ },
+ {
+ name: 'test',
+ groups: [
+ {
+ name: 'test_1',
+ jobs: [{ script: 'yarn test', stage: 'test' }],
+ },
+ {
+ name: 'test_2',
+ jobs: [{ script: 'yarn karma', stage: 'test' }],
+ },
+ ],
+ },
+ {
+ name: 'deploy',
+ groups: [
+ {
+ name: 'deploy_1',
+ jobs: [{ script: 'yarn magick', stage: 'deploy' }],
+ },
+ ],
+ },
+ ],
+};
diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
new file mode 100644
index 00000000000..30e192e5726
--- /dev/null
+++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
@@ -0,0 +1,59 @@
+import { shallowMount } from '@vue/test-utils';
+import { pipelineData } from './mock_data';
+import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
+import StagePill from '~/pipelines/components/pipeline_graph/stage_pill.vue';
+import JobPill from '~/pipelines/components/pipeline_graph/job_pill.vue';
+
+describe('pipeline graph component', () => {
+ const defaultProps = { pipelineData };
+ let wrapper;
+
+ const createComponent = props => {
+ return shallowMount(PipelineGraph, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ const findAllStagePills = () => wrapper.findAll(StagePill);
+ const findAllJobPills = () => wrapper.findAll(JobPill);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('with no data', () => {
+ beforeEach(() => {
+ wrapper = createComponent({ pipelineData: {} });
+ });
+
+ it('renders an empty section', () => {
+ expect(wrapper.text()).toContain('No content to show');
+ expect(findAllStagePills()).toHaveLength(0);
+ expect(findAllJobPills()).toHaveLength(0);
+ });
+ });
+
+ describe('with data', () => {
+ beforeEach(() => {
+ wrapper = createComponent();
+ });
+ it('renders the right number of stage pills', () => {
+ const expectedStagesLength = pipelineData.stages.length;
+
+ expect(findAllStagePills()).toHaveLength(expectedStagesLength);
+ });
+
+ it('renders the right number of job pills', () => {
+ // We count the number of jobs in the mock data
+ const expectedJobsLength = pipelineData.stages.reduce((acc, val) => {
+ return acc + val.groups.length;
+ }, 0);
+
+ expect(findAllJobPills()).toHaveLength(expectedJobsLength);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/pipeline_graph/utils_spec.js b/spec/frontend/pipelines/pipeline_graph/utils_spec.js
new file mode 100644
index 00000000000..dd85c8c2bd0
--- /dev/null
+++ b/spec/frontend/pipelines/pipeline_graph/utils_spec.js
@@ -0,0 +1,150 @@
+import { preparePipelineGraphData } from '~/pipelines/utils';
+
+describe('preparePipelineGraphData', () => {
+ const emptyResponse = { stages: [] };
+ const jobName1 = 'build_1';
+ const jobName2 = 'build_2';
+ const jobName3 = 'test_1';
+ const jobName4 = 'deploy_1';
+ const job1 = { [jobName1]: { script: 'echo hello', stage: 'build' } };
+ const job2 = { [jobName2]: { script: 'echo build', stage: 'build' } };
+ const job3 = { [jobName3]: { script: 'echo test', stage: 'test' } };
+ const job4 = { [jobName4]: { script: 'echo deploy', stage: 'deploy' } };
+
+ describe('returns an object with an empty array of stages if', () => {
+ it('no data is passed', () => {
+ expect(preparePipelineGraphData({})).toEqual(emptyResponse);
+ });
+
+ it('no stages are found', () => {
+ expect(preparePipelineGraphData({ includes: 'template/myTemplate.gitlab-ci.yml' })).toEqual(
+ emptyResponse,
+ );
+ });
+ });
+
+ describe('returns the correct array of stages', () => {
+ it('when multiple jobs are in the same stage', () => {
+ const expectedData = {
+ stages: [
+ {
+ name: job1[jobName1].stage,
+ groups: [
+ {
+ name: jobName1,
+ jobs: [{ script: job1[jobName1].script, stage: job1[jobName1].stage }],
+ },
+ {
+ name: jobName2,
+ jobs: [{ script: job2[jobName2].script, stage: job2[jobName2].stage }],
+ },
+ ],
+ },
+ ],
+ };
+
+ expect(preparePipelineGraphData({ ...job1, ...job2 })).toEqual(expectedData);
+ });
+
+ it('when stages are defined by the user', () => {
+ const userDefinedStage = 'myStage';
+ const userDefinedStage2 = 'myStage2';
+
+ const expectedData = {
+ stages: [
+ {
+ name: userDefinedStage,
+ groups: [],
+ },
+ {
+ name: userDefinedStage2,
+ groups: [],
+ },
+ ],
+ };
+
+ expect(preparePipelineGraphData({ stages: [userDefinedStage, userDefinedStage2] })).toEqual(
+ expectedData,
+ );
+ });
+
+ it('by combining user defined stage and job stages, it preserves user defined order', () => {
+ const userDefinedStage = 'myStage';
+ const userDefinedStageThatOverlaps = 'deploy';
+
+ const expectedData = {
+ stages: [
+ {
+ name: userDefinedStage,
+ groups: [],
+ },
+ {
+ name: job4[jobName4].stage,
+ groups: [
+ {
+ name: jobName4,
+ jobs: [{ script: job4[jobName4].script, stage: job4[jobName4].stage }],
+ },
+ ],
+ },
+ {
+ name: job1[jobName1].stage,
+ groups: [
+ {
+ name: jobName1,
+ jobs: [{ script: job1[jobName1].script, stage: job1[jobName1].stage }],
+ },
+ {
+ name: jobName2,
+ jobs: [{ script: job2[jobName2].script, stage: job2[jobName2].stage }],
+ },
+ ],
+ },
+ {
+ name: job3[jobName3].stage,
+ groups: [
+ {
+ name: jobName3,
+ jobs: [{ script: job3[jobName3].script, stage: job3[jobName3].stage }],
+ },
+ ],
+ },
+ ],
+ };
+
+ expect(
+ preparePipelineGraphData({
+ stages: [userDefinedStage, userDefinedStageThatOverlaps],
+ ...job1,
+ ...job2,
+ ...job3,
+ ...job4,
+ }),
+ ).toEqual(expectedData);
+ });
+
+ it('with only unique values', () => {
+ const expectedData = {
+ stages: [
+ {
+ name: job1[jobName1].stage,
+ groups: [
+ {
+ name: jobName1,
+ jobs: [{ script: job1[jobName1].script, stage: job1[jobName1].stage }],
+ },
+ ],
+ },
+ ],
+ };
+
+ expect(
+ preparePipelineGraphData({
+ stages: ['build'],
+ ...job1,
+ ...job1,
+ }),
+ ).toEqual(expectedData);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/pipeline_triggerer_spec.js b/spec/frontend/pipelines/pipeline_triggerer_spec.js
index 6fd9a143d82..ad8136890e6 100644
--- a/spec/frontend/pipelines/pipeline_triggerer_spec.js
+++ b/spec/frontend/pipelines/pipeline_triggerer_spec.js
@@ -36,7 +36,7 @@ describe('Pipelines Triggerer', () => {
});
it('should render a table cell', () => {
- expect(wrapper.contains('.table-section')).toBe(true);
+ expect(wrapper.find('.table-section').exists()).toBe(true);
});
it('should pass triggerer information when triggerer is provided', () => {
diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js
index cce4c2dfa7b..071a2b24889 100644
--- a/spec/frontend/pipelines/pipelines_actions_spec.js
+++ b/spec/frontend/pipelines/pipelines_actions_spec.js
@@ -1,7 +1,7 @@
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 { GlButton } 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';
@@ -19,7 +19,7 @@ describe('Pipelines Actions dropdown', () => {
});
};
- const findAllDropdownItems = () => wrapper.findAll(GlDeprecatedButton);
+ const findAllDropdownItems = () => wrapper.findAll(GlButton);
const findAllCountdowns = () => wrapper.findAll(GlCountdown);
beforeEach(() => {
@@ -66,7 +66,7 @@ describe('Pipelines Actions dropdown', () => {
it('makes a request and toggles the loading state', () => {
mock.onPost(mockActions.path).reply(200);
- wrapper.find(GlDeprecatedButton).vm.$emit('click');
+ wrapper.find(GlButton).vm.$emit('click');
expect(wrapper.vm.isLoading).toBe(true);
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
index ca9ebb54138..58e8065033f 100644
--- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
@@ -1,6 +1,6 @@
import { getJSONFixture } from 'helpers/fixtures';
import * as getters from '~/pipelines/stores/test_reports/getters';
-import { iconForTestStatus } from '~/pipelines/stores/test_reports/utils';
+import { iconForTestStatus, formattedTime } from '~/pipelines/stores/test_reports/utils';
describe('Getters TestReports Store', () => {
let state;
@@ -34,7 +34,7 @@ describe('Getters TestReports Store', () => {
const suites = getters.getTestSuites(state);
const expected = testReports.test_suites.map(x => ({
...x,
- formattedTime: '00:00:00',
+ formattedTime: formattedTime(x.total_time),
}));
expect(suites).toEqual(expected);
@@ -65,7 +65,7 @@ describe('Getters TestReports Store', () => {
const cases = getters.getSuiteTests(state);
const expected = testReports.test_suites[0].test_cases.map(x => ({
...x,
- formattedTime: '00:00:00',
+ formattedTime: formattedTime(x.execution_time),
icon: iconForTestStatus(x.status),
}));
diff --git a/spec/frontend/pipelines/test_reports/stores/utils_spec.js b/spec/frontend/pipelines/test_reports/stores/utils_spec.js
new file mode 100644
index 00000000000..7e632d099fc
--- /dev/null
+++ b/spec/frontend/pipelines/test_reports/stores/utils_spec.js
@@ -0,0 +1,26 @@
+import { formattedTime } from '~/pipelines/stores/test_reports/utils';
+
+describe('Test reports utils', () => {
+ describe('formattedTime', () => {
+ describe('when time is smaller than a second', () => {
+ it('should return time in milliseconds fixed to 2 decimals', () => {
+ const result = formattedTime(0.4815162342);
+ expect(result).toBe('481.52ms');
+ });
+ });
+
+ describe('when time is equal to a second', () => {
+ it('should return time in seconds fixed to 2 decimals', () => {
+ const result = formattedTime(1);
+ expect(result).toBe('1.00s');
+ });
+ });
+
+ describe('when time is greater than a second', () => {
+ it('should return time in seconds fixed to 2 decimals', () => {
+ const result = formattedTime(4.815162342);
+ expect(result).toBe('4.82s');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js
index a709edf5184..c8ab18b9086 100644
--- a/spec/frontend/pipelines/test_reports/test_reports_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_reports_spec.js
@@ -1,4 +1,5 @@
import Vuex from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
@@ -15,9 +16,9 @@ describe('Test reports app', () => {
const testReports = getJSONFixture('pipelines/test_report.json');
- const loadingSpinner = () => wrapper.find('.js-loading-spinner');
- const testsDetail = () => wrapper.find('.js-tests-detail');
- const noTestsToShow = () => wrapper.find('.js-no-tests-to-show');
+ const loadingSpinner = () => wrapper.find(GlLoadingIcon);
+ const testsDetail = () => wrapper.find('[data-testid="tests-detail"]');
+ const noTestsToShow = () => wrapper.find('[data-testid="no-tests-to-show"]');
const testSummary = () => wrapper.find(TestSummary);
const testSummaryTable = () => wrapper.find(TestSummaryTable);
@@ -88,6 +89,10 @@ describe('Test reports app', () => {
expect(wrapper.vm.testReports).toBeTruthy();
expect(wrapper.vm.showTests).toBeTruthy();
});
+
+ it('shows tests details', () => {
+ expect(testsDetail().exists()).toBe(true);
+ });
});
describe('when a suite is clicked', () => {
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 3a4aa94571e..2feb6aa5799 100644
--- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
@@ -23,8 +23,6 @@ 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) => {
@@ -63,16 +61,6 @@ describe('Test reports suite table', () => {
expect(allCaseRows().length).toBe(testCases.length);
});
- 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);
-
- expect(allCaseNames()).toEqual(expectedCaseOrder);
- });
-
it('renders the correct icon for each status', () => {
const failedTest = testCases.findIndex(x => x.status === TestStatus.FAILED);
const skippedTest = testCases.findIndex(x => x.status === TestStatus.SKIPPED);
diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js
index 79be6c168cf..dc5af7b160c 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_spec.js
@@ -1,6 +1,7 @@
import { mount } from '@vue/test-utils';
import { getJSONFixture } from 'helpers/fixtures';
import Summary from '~/pipelines/components/test_reports/test_summary.vue';
+import { formattedTime } from '~/pipelines/stores/test_reports/utils';
describe('Test reports summary', () => {
let wrapper;
@@ -76,7 +77,7 @@ describe('Test reports summary', () => {
});
it('displays the correctly formatted duration', () => {
- expect(duration().text()).toBe('00:00:00');
+ expect(duration().text()).toBe(formattedTime(testSuite.total_time));
});
});
diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js
index 04934fb93b0..b7bc8d08a0f 100644
--- a/spec/frontend/pipelines/time_ago_spec.js
+++ b/spec/frontend/pipelines/time_ago_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
import TimeAgo from '~/pipelines/components/pipelines_list/time_ago.vue';
describe('Timeago component', () => {
@@ -22,14 +23,19 @@ describe('Timeago component', () => {
wrapper = null;
});
+ const duration = () => wrapper.find('.duration');
+ const finishedAt = () => wrapper.find('.finished-at');
+
describe('with duration', () => {
beforeEach(() => {
createComponent({ duration: 10, finishedTime: '' });
});
it('should render duration and timer svg', () => {
- expect(wrapper.find('.duration').exists()).toBe(true);
- expect(wrapper.find('.duration svg').exists()).toBe(true);
+ const icon = duration().find(GlIcon);
+
+ expect(duration().exists()).toBe(true);
+ expect(icon.props('name')).toBe('timer');
});
});
@@ -39,7 +45,7 @@ describe('Timeago component', () => {
});
it('should not render duration and timer svg', () => {
- expect(wrapper.find('.duration').exists()).toBe(false);
+ expect(duration().exists()).toBe(false);
});
});
@@ -49,9 +55,12 @@ describe('Timeago component', () => {
});
it('should render time and calendar icon', () => {
- expect(wrapper.find('.finished-at').exists()).toBe(true);
- expect(wrapper.find('.finished-at i.fa-calendar').exists()).toBe(true);
- expect(wrapper.find('.finished-at time').exists()).toBe(true);
+ const icon = finishedAt().find(GlIcon);
+ const time = finishedAt().find('time');
+
+ expect(finishedAt().exists()).toBe(true);
+ expect(icon.props('name')).toBe('calendar');
+ expect(time.exists()).toBe(true);
});
});
@@ -61,7 +70,7 @@ describe('Timeago component', () => {
});
it('should not render time and calendar icon', () => {
- expect(wrapper.find('.finished-at').exists()).toBe(false);
+ expect(finishedAt().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
index 096e4cd97f6..b53955ab743 100644
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
@@ -5,16 +5,17 @@ import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pi
describe('Pipeline Status Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findAllGlIcons = () => wrapper.findAll(GlIcon);
-
const stubs = {
GlFilteredSearchToken: {
+ props: GlFilteredSearchToken.props,
template: `<div><slot name="suggestions"></slot></div>`,
},
};
+ const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
+ const findAllGlIcons = () => wrapper.findAll(GlIcon);
+
const defaultProps = {
config: {
type: 'status',
@@ -27,12 +28,12 @@ describe('Pipeline Status Token', () => {
},
};
- const createComponent = options => {
+ const createComponent = () => {
wrapper = shallowMount(PipelineStatusToken, {
propsData: {
...defaultProps,
},
- ...options,
+ stubs,
});
};
@@ -50,10 +51,6 @@ describe('Pipeline Status Token', () => {
});
describe('shows statuses correctly', () => {
- beforeEach(() => {
- createComponent({ stubs });
- });
-
it('renders all pipeline statuses available', () => {
expect(findAllFilteredSearchSuggestions()).toHaveLength(wrapper.vm.statuses.length);
expect(findAllGlIcons()).toHaveLength(wrapper.vm.statuses.length);
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 c95d2ea1b7b..9363944a719 100644
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
@@ -7,16 +7,17 @@ import { users } from '../mock_data';
describe('Pipeline Trigger Author Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
-
const stubs = {
GlFilteredSearchToken: {
+ props: GlFilteredSearchToken.props,
template: `<div><slot name="suggestions"></slot></div>`,
},
};
+ const findFilteredSearchToken = () => wrapper.find(stubs.GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+
const defaultProps = {
config: {
type: 'username',
@@ -31,7 +32,7 @@ describe('Pipeline Trigger Author Token', () => {
},
};
- const createComponent = (options, data) => {
+ const createComponent = data => {
wrapper = shallowMount(PipelineTriggerAuthorToken, {
propsData: {
...defaultProps,
@@ -41,7 +42,7 @@ describe('Pipeline Trigger Author Token', () => {
...data,
};
},
- ...options,
+ stubs,
});
};
@@ -69,13 +70,13 @@ describe('Pipeline Trigger Author Token', () => {
describe('displays loading icon correctly', () => {
it('shows loading icon', () => {
- createComponent({ stubs }, { loading: true });
+ createComponent({ loading: true });
expect(findLoadingIcon().exists()).toBe(true);
});
it('does not show loading icon', () => {
- createComponent({ stubs }, { loading: false });
+ createComponent({ loading: false });
expect(findLoadingIcon().exists()).toBe(false);
});
@@ -85,22 +86,17 @@ describe('Pipeline Trigger Author Token', () => {
beforeEach(() => {});
it('renders all trigger authors', () => {
- createComponent({ stubs }, { users, loading: false });
+ createComponent({ users, loading: false });
// should have length of all users plus the static 'Any' option
expect(findAllFilteredSearchSuggestions()).toHaveLength(users.length + 1);
});
it('renders only the trigger author searched for', () => {
- createComponent(
- { stubs },
- {
- users: [
- { name: 'Arnold', username: 'admin', state: 'active', avatar_url: 'avatar-link' },
- ],
- loading: false,
- },
- );
+ createComponent({
+ users: [{ name: 'Arnold', username: 'admin', state: 'active', avatar_url: 'avatar-link' }],
+ loading: false,
+ });
expect(findAllFilteredSearchSuggestions()).toHaveLength(2);
});