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:
Diffstat (limited to 'spec/frontend/jobs')
-rw-r--r--spec/frontend/jobs/components/commit_block_spec.js105
-rw-r--r--spec/frontend/jobs/components/job_sidebar_details_container_spec.js15
-rw-r--r--spec/frontend/jobs/components/manual_variables_form_spec.js153
-rw-r--r--spec/frontend/jobs/components/sidebar_spec.js30
-rw-r--r--spec/frontend/jobs/components/stages_dropdown_spec.js155
-rw-r--r--spec/frontend/jobs/components/table/jobs_table_spec.js31
-rw-r--r--spec/frontend/jobs/components/table/jobs_table_tabs_spec.js42
-rw-r--r--spec/frontend/jobs/mock_data.js215
8 files changed, 524 insertions, 222 deletions
diff --git a/spec/frontend/jobs/components/commit_block_spec.js b/spec/frontend/jobs/components/commit_block_spec.js
index 13261317b48..8a6d48cecb8 100644
--- a/spec/frontend/jobs/components/commit_block_spec.js
+++ b/spec/frontend/jobs/components/commit_block_spec.js
@@ -1,89 +1,70 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import component from '~/jobs/components/commit_block.vue';
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import CommitBlock from '~/jobs/components/commit_block.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('Commit block', () => {
- const Component = Vue.extend(component);
- let vm;
+ let wrapper;
- const props = {
- commit: {
- short_id: '1f0fb84f',
- id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
- commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
- title: 'Update README.md',
- },
- mergeRequest: {
- iid: '!21244',
- path: 'merge_requests/21244',
- },
- isLastBlock: true,
+ const commit = {
+ short_id: '1f0fb84f',
+ id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
+ commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
+ title: 'Update README.md',
+ };
+
+ const mergeRequest = {
+ iid: '!21244',
+ path: 'merge_requests/21244',
+ };
+
+ const findCommitSha = () => wrapper.findByTestId('commit-sha');
+ const findLinkSha = () => wrapper.findByTestId('link-commit');
+
+ const mountComponent = (props) => {
+ wrapper = extendedWrapper(
+ shallowMount(CommitBlock, {
+ propsData: {
+ commit,
+ ...props,
+ },
+ }),
+ );
};
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
- describe('pipeline short sha', () => {
+ describe('without merge request', () => {
beforeEach(() => {
- vm = mountComponent(Component, {
- ...props,
- });
+ mountComponent();
});
it('renders pipeline short sha link', () => {
- expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual(
- props.commit.commit_path,
- );
-
- expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual(
- props.commit.short_id,
- );
+ expect(findCommitSha().attributes('href')).toBe(commit.commit_path);
+ expect(findCommitSha().text()).toBe(commit.short_id);
});
it('renders clipboard button', () => {
- expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual(
- props.commit.id,
- );
+ expect(wrapper.findComponent(ClipboardButton).attributes('text')).toBe(commit.id);
});
- });
-
- describe('with merge request', () => {
- it('renders merge request link and reference', () => {
- vm = mountComponent(Component, {
- ...props,
- });
-
- expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual(
- props.mergeRequest.path,
- );
- expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual(
- `!${props.mergeRequest.iid}`,
- );
+ it('renders git commit title', () => {
+ expect(wrapper.text()).toContain(commit.title);
});
- });
- describe('without merge request', () => {
it('does not render merge request', () => {
- const copyProps = { ...props };
- delete copyProps.mergeRequest;
-
- vm = mountComponent(Component, {
- ...copyProps,
- });
-
- expect(vm.$el.querySelector('.js-link-commit')).toBeNull();
+ expect(findLinkSha().exists()).toBe(false);
});
});
- describe('git commit title', () => {
- it('renders git commit title', () => {
- vm = mountComponent(Component, {
- ...props,
- });
+ describe('with merge request', () => {
+ it('renders merge request link and reference', () => {
+ mountComponent({ mergeRequest });
- expect(vm.$el.textContent).toContain(props.commit.title);
+ expect(findLinkSha().attributes('href')).toBe(mergeRequest.path);
+ expect(findLinkSha().text()).toBe(`!${mergeRequest.iid}`);
});
});
});
diff --git a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js b/spec/frontend/jobs/components/job_sidebar_details_container_spec.js
index 2b56bd2d558..ad0368555fa 100644
--- a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js
+++ b/spec/frontend/jobs/components/job_sidebar_details_container_spec.js
@@ -34,11 +34,22 @@ describe('Job Sidebar Details Container', () => {
});
describe('when no details are available', () => {
- it('should render an empty container', () => {
+ beforeEach(() => {
createWrapper();
+ });
+ it('should render an empty container', () => {
expect(wrapper.html()).toBe('');
});
+
+ it.each(['duration', 'erased_at', 'finished_at', 'queued', 'runner', 'coverage'])(
+ 'should not render %s details when missing',
+ async (detail) => {
+ await store.dispatch('receiveJobSuccess', { [detail]: undefined });
+
+ expect(findAllDetailsRow()).toHaveLength(0);
+ },
+ );
});
describe('when some of the details are available', () => {
@@ -49,7 +60,7 @@ describe('Job Sidebar Details Container', () => {
['erased_at', 'Erased: 3 weeks ago'],
['finished_at', 'Finished: 3 weeks ago'],
['queued', 'Queued: 9 seconds'],
- ['runner', 'Runner: local ci runner (#1)'],
+ ['runner', 'Runner: #1 (ABCDEFGH) local ci runner'],
['coverage', 'Coverage: 20%'],
])('uses %s to render job-%s', async (detail, value) => {
await store.dispatch('receiveJobSuccess', { [detail]: job[detail] });
diff --git a/spec/frontend/jobs/components/manual_variables_form_spec.js b/spec/frontend/jobs/components/manual_variables_form_spec.js
index 7172a319876..376a822dde5 100644
--- a/spec/frontend/jobs/components/manual_variables_form_spec.js
+++ b/spec/frontend/jobs/components/manual_variables_form_spec.js
@@ -1,11 +1,16 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import Form from '~/jobs/components/manual_variables_form.vue';
const localVue = createLocalVue();
+Vue.use(Vuex);
+
describe('Manual Variables Form', () => {
let wrapper;
+ let store;
const requiredProps = {
action: {
@@ -16,88 +21,104 @@ describe('Manual Variables Form', () => {
variablesSettingsUrl: '/settings',
};
- const factory = (props = {}) => {
- wrapper = shallowMount(localVue.extend(Form), {
- propsData: props,
- localVue,
+ const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
+ store = new Vuex.Store({
+ actions: {
+ triggerManualJob: jest.fn(),
+ },
});
+
+ wrapper = extendedWrapper(
+ mountFn(localVue.extend(Form), {
+ propsData: { ...requiredProps, ...props },
+ localVue,
+ store,
+ }),
+ );
};
- beforeEach(() => {
- factory(requiredProps);
- });
+ const findInputKey = () => wrapper.findComponent({ ref: 'inputKey' });
+ const findInputValue = () => wrapper.findComponent({ ref: 'inputSecretValue' });
- afterEach((done) => {
- // The component has a `nextTick` callback after some events so we need
- // to wait for those to finish before destroying.
- setImmediate(() => {
- wrapper.destroy();
- wrapper = null;
+ const findTriggerBtn = () => wrapper.findByTestId('trigger-manual-job-btn');
+ const findHelpText = () => wrapper.findByTestId('form-help-text');
+ const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn');
+ const findCiVariableKey = () => wrapper.findByTestId('ci-variable-key');
+ const findCiVariableValue = () => wrapper.findByTestId('ci-variable-value');
+ const findAllVariables = () => wrapper.findAllByTestId('ci-variable-row');
- done();
- });
+ afterEach(() => {
+ wrapper.destroy();
});
- it('renders empty form with correct placeholders', () => {
- expect(wrapper.find({ ref: 'inputKey' }).attributes('placeholder')).toBe('Input variable key');
- expect(wrapper.find({ ref: 'inputSecretValue' }).attributes('placeholder')).toBe(
- 'Input variable value',
- );
- });
+ describe('shallowMount', () => {
+ beforeEach(() => {
+ createComponent();
+ });
- it('renders help text with provided link', () => {
- expect(wrapper.find('p').text()).toBe(
- 'Specify variable values to be used in this run. The values specified in CI/CD settings will be used as default',
- );
+ it('renders empty form with correct placeholders', () => {
+ expect(findInputKey().attributes('placeholder')).toBe('Input variable key');
+ expect(findInputValue().attributes('placeholder')).toBe('Input variable value');
+ });
- expect(wrapper.find('a').attributes('href')).toBe(requiredProps.variablesSettingsUrl);
- });
+ it('renders help text with provided link', () => {
+ expect(findHelpText().text()).toBe(
+ 'Specify variable values to be used in this run. The values specified in CI/CD settings will be used as default',
+ );
- describe('when adding a new variable', () => {
- it('creates a new variable when user types a new key and resets the form', (done) => {
- wrapper.vm
- .$nextTick()
- .then(() => wrapper.find({ ref: 'inputKey' }).setValue('new key'))
- .then(() => {
- expect(wrapper.vm.variables.length).toBe(1);
- expect(wrapper.vm.variables[0].key).toBe('new key');
- expect(wrapper.find({ ref: 'inputKey' }).attributes('value')).toBe(undefined);
- })
- .then(done)
- .catch(done.fail);
+ expect(wrapper.find('a').attributes('href')).toBe(requiredProps.variablesSettingsUrl);
});
- it('creates a new variable when user types a new value and resets the form', (done) => {
- wrapper.vm
- .$nextTick()
- .then(() => wrapper.find({ ref: 'inputSecretValue' }).setValue('new value'))
- .then(() => {
- expect(wrapper.vm.variables.length).toBe(1);
- expect(wrapper.vm.variables[0].secret_value).toBe('new value');
- expect(wrapper.find({ ref: 'inputSecretValue' }).attributes('value')).toBe(undefined);
- })
- .then(done)
- .catch(done.fail);
+ describe('when adding a new variable', () => {
+ it('creates a new variable when user types a new key and resets the form', async () => {
+ await findInputKey().setValue('new key');
+
+ expect(findAllVariables()).toHaveLength(1);
+ expect(findCiVariableKey().element.value).toBe('new key');
+ expect(findInputKey().attributes('value')).toBe(undefined);
+ });
+
+ it('creates a new variable when user types a new value and resets the form', async () => {
+ await findInputValue().setValue('new value');
+
+ expect(findAllVariables()).toHaveLength(1);
+ expect(findCiVariableValue().element.value).toBe('new value');
+ expect(findInputValue().attributes('value')).toBe(undefined);
+ });
});
});
- describe('when deleting a variable', () => {
- beforeEach((done) => {
- wrapper.vm.variables = [
- {
- key: 'new key',
- secret_value: 'value',
- id: '1',
- },
- ];
-
- wrapper.vm.$nextTick(done);
+ describe('mount', () => {
+ beforeEach(() => {
+ createComponent({ mountFn: mount });
+ });
+
+ describe('when deleting a variable', () => {
+ it('removes the variable row', async () => {
+ await wrapper.setData({
+ variables: [
+ {
+ key: 'new key',
+ secret_value: 'value',
+ id: '1',
+ },
+ ],
+ });
+
+ findDeleteVarBtn().trigger('click');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findAllVariables()).toHaveLength(0);
+ });
});
- it('removes the variable row', () => {
- wrapper.find(GlButton).vm.$emit('click');
+ it('trigger button is disabled after trigger action', async () => {
+ expect(findTriggerBtn().props('disabled')).toBe(false);
+
+ await findTriggerBtn().trigger('click');
- expect(wrapper.vm.variables.length).toBe(0);
+ expect(findTriggerBtn().props('disabled')).toBe(true);
});
});
});
diff --git a/spec/frontend/jobs/components/sidebar_spec.js b/spec/frontend/jobs/components/sidebar_spec.js
index 5a2e699137d..500a1b48950 100644
--- a/spec/frontend/jobs/components/sidebar_spec.js
+++ b/spec/frontend/jobs/components/sidebar_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import ArtifactsBlock from '~/jobs/components/artifacts_block.vue';
import JobRetryForwardDeploymentModal from '~/jobs/components/job_retry_forward_deployment_modal.vue';
import JobRetryButton from '~/jobs/components/job_sidebar_retry_button.vue';
import JobsContainer from '~/jobs/components/jobs_container.vue';
@@ -14,6 +15,7 @@ describe('Sidebar details block', () => {
const forwardDeploymentFailure = 'forward_deployment_failure';
const findModal = () => wrapper.find(JobRetryForwardDeploymentModal);
+ const findArtifactsBlock = () => wrapper.findComponent(ArtifactsBlock);
const findCancelButton = () => wrapper.findByTestId('cancel-button');
const findNewIssueButton = () => wrapper.findByTestId('job-new-issue');
const findRetryButton = () => wrapper.find(JobRetryButton);
@@ -21,6 +23,9 @@ describe('Sidebar details block', () => {
const createWrapper = ({ props = {} } = {}) => {
store = createStore();
+
+ store.state.job = job;
+
wrapper = extendedWrapper(
shallowMount(Sidebar, {
...props,
@@ -164,4 +169,29 @@ describe('Sidebar details block', () => {
});
});
});
+
+ describe('artifacts', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('artifacts are not shown if there are no properties other than locked', () => {
+ expect(findArtifactsBlock().exists()).toBe(false);
+ });
+
+ it('artifacts are shown if present', async () => {
+ store.state.job.artifact = {
+ download_path: '/root/ci-project/-/jobs/1960/artifacts/download',
+ browse_path: '/root/ci-project/-/jobs/1960/artifacts/browse',
+ keep_path: '/root/ci-project/-/jobs/1960/artifacts/keep',
+ expire_at: '2021-03-23T17:57:11.211Z',
+ expired: false,
+ locked: false,
+ };
+
+ await wrapper.vm.$nextTick();
+
+ expect(findArtifactsBlock().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/jobs/components/stages_dropdown_spec.js b/spec/frontend/jobs/components/stages_dropdown_spec.js
index 72d5d0f9d44..b75d1707a8d 100644
--- a/spec/frontend/jobs/components/stages_dropdown_spec.js
+++ b/spec/frontend/jobs/components/stages_dropdown_spec.js
@@ -1,163 +1,134 @@
-import Vue from 'vue';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import component from '~/jobs/components/stages_dropdown.vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import StagesDropdown from '~/jobs/components/stages_dropdown.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import {
+ mockPipelineWithoutMR,
+ mockPipelineWithAttachedMR,
+ mockPipelineDetached,
+} from '../mock_data';
describe('Stages Dropdown', () => {
- const Component = Vue.extend(component);
- let vm;
-
- const mockPipelineData = {
- id: 28029444,
- details: {
- status: {
- details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
- group: 'success',
- has_details: true,
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- },
- path: 'pipeline/28029444',
- flags: {
- merge_request_pipeline: true,
- detached_merge_request_pipeline: false,
- },
- merge_request: {
- iid: 1234,
- path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
- title: 'Update README.md',
- source_branch: 'feature-1234',
- source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
- target_branch: 'master',
- target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
- },
- ref: {
- name: 'test-branch',
- },
+ let wrapper;
+
+ const findStatus = () => wrapper.findComponent(CiIcon);
+ const findSelectedStageText = () => wrapper.findComponent(GlDropdown).props('text');
+ const findStageItem = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
+
+ const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
+ const findPipelinePath = () => wrapper.findByTestId('pipeline-path').attributes('href');
+ const findMRLinkPath = () => wrapper.findByTestId('mr-link').attributes('href');
+ const findSourceBranchLinkPath = () =>
+ wrapper.findByTestId('source-branch-link').attributes('href');
+ const findTargetBranchLinkPath = () =>
+ wrapper.findByTestId('target-branch-link').attributes('href');
+
+ const createComponent = (props) => {
+ wrapper = extendedWrapper(
+ shallowMount(StagesDropdown, {
+ propsData: {
+ ...props,
+ },
+ }),
+ );
};
- describe('without a merge request pipeline', () => {
- let pipeline;
+ afterEach(() => {
+ wrapper.destroy();
+ });
+ describe('without a merge request pipeline', () => {
beforeEach(() => {
- pipeline = JSON.parse(JSON.stringify(mockPipelineData));
- delete pipeline.merge_request;
- delete pipeline.flags.merge_request_pipeline;
- delete pipeline.flags.detached_merge_request_pipeline;
-
- vm = mountComponent(Component, {
- pipeline,
+ createComponent({
+ pipeline: mockPipelineWithoutMR,
stages: [{ name: 'build' }, { name: 'test' }],
selectedStage: 'deploy',
});
});
- afterEach(() => {
- vm.$destroy();
- });
-
it('renders pipeline status', () => {
- expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
+ expect(findStatus().exists()).toBe(true);
});
it('renders pipeline link', () => {
- expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
- 'pipeline/28029444',
- );
+ expect(findPipelinePath()).toBe('pipeline/28029444');
});
it('renders dropdown with stages', () => {
- expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
+ expect(findStageItem(0).text()).toBe('build');
});
it('rendes selected stage', () => {
- expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
+ expect(findSelectedStageText()).toBe('deploy');
});
it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => {
- const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`;
- const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+ const expected = `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`;
+ const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
});
describe('with an "attached" merge request pipeline', () => {
- let pipeline;
-
beforeEach(() => {
- pipeline = JSON.parse(JSON.stringify(mockPipelineData));
- pipeline.flags.merge_request_pipeline = true;
- pipeline.flags.detached_merge_request_pipeline = false;
-
- vm = mountComponent(Component, {
- pipeline,
+ createComponent({
+ pipeline: mockPipelineWithAttachedMR,
stages: [],
selectedStage: 'deploy',
});
});
it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => {
- const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${pipeline.merge_request.target_branch}`;
- const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+ const expected = `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`;
+ const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
it(`renders the correct merge request link`, () => {
- const actual = vm.$el.querySelector('.js-mr-link').href;
-
- expect(actual).toContain(pipeline.merge_request.path);
+ expect(findMRLinkPath()).toBe(mockPipelineWithAttachedMR.merge_request.path);
});
it(`renders the correct source branch link`, () => {
- const actual = vm.$el.querySelector('.js-source-branch-link').href;
-
- expect(actual).toContain(pipeline.merge_request.source_branch_path);
+ expect(findSourceBranchLinkPath()).toBe(
+ mockPipelineWithAttachedMR.merge_request.source_branch_path,
+ );
});
it(`renders the correct target branch link`, () => {
- const actual = vm.$el.querySelector('.js-target-branch-link').href;
-
- expect(actual).toContain(pipeline.merge_request.target_branch_path);
+ expect(findTargetBranchLinkPath()).toBe(
+ mockPipelineWithAttachedMR.merge_request.target_branch_path,
+ );
});
});
describe('with a detached merge request pipeline', () => {
- let pipeline;
-
beforeEach(() => {
- pipeline = JSON.parse(JSON.stringify(mockPipelineData));
- pipeline.flags.merge_request_pipeline = false;
- pipeline.flags.detached_merge_request_pipeline = true;
-
- vm = mountComponent(Component, {
- pipeline,
+ createComponent({
+ pipeline: mockPipelineDetached,
stages: [],
selectedStage: 'deploy',
});
});
it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => {
- const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`;
- const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+ const expected = `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`;
+ const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected);
});
it(`renders the correct merge request link`, () => {
- const actual = vm.$el.querySelector('.js-mr-link').href;
-
- expect(actual).toContain(pipeline.merge_request.path);
+ expect(findMRLinkPath()).toBe(mockPipelineDetached.merge_request.path);
});
it(`renders the correct source branch link`, () => {
- const actual = vm.$el.querySelector('.js-source-branch-link').href;
-
- expect(actual).toContain(pipeline.merge_request.source_branch_path);
+ expect(findSourceBranchLinkPath()).toBe(
+ mockPipelineDetached.merge_request.source_branch_path,
+ );
});
});
});
diff --git a/spec/frontend/jobs/components/table/jobs_table_spec.js b/spec/frontend/jobs/components/table/jobs_table_spec.js
new file mode 100644
index 00000000000..db057efbfb4
--- /dev/null
+++ b/spec/frontend/jobs/components/table/jobs_table_spec.js
@@ -0,0 +1,31 @@
+import { GlTable } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import JobsTable from '~/jobs/components/table/jobs_table.vue';
+import { mockJobsInTable } from '../../mock_data';
+
+describe('Jobs Table', () => {
+ let wrapper;
+
+ const findTable = () => wrapper.findComponent(GlTable);
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(JobsTable, {
+ propsData: {
+ jobs: mockJobsInTable,
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('displays a table', () => {
+ expect(findTable().exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js b/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js
new file mode 100644
index 00000000000..ac9b45be932
--- /dev/null
+++ b/spec/frontend/jobs/components/table/jobs_table_tabs_spec.js
@@ -0,0 +1,42 @@
+import { mount } from '@vue/test-utils';
+import { trimText } from 'helpers/text_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
+
+describe('Jobs Table Tabs', () => {
+ let wrapper;
+
+ const defaultProps = {
+ jobCounts: { all: 848, pending: 0, running: 0, finished: 704 },
+ };
+
+ const findTab = (testId) => wrapper.findByTestId(testId);
+
+ const createComponent = () => {
+ wrapper = extendedWrapper(
+ mount(JobsTableTabs, {
+ provide: {
+ ...defaultProps,
+ },
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ tabId | text | count
+ ${'jobs-all-tab'} | ${'All'} | ${defaultProps.jobCounts.all}
+ ${'jobs-pending-tab'} | ${'Pending'} | ${defaultProps.jobCounts.pending}
+ ${'jobs-running-tab'} | ${'Running'} | ${defaultProps.jobCounts.running}
+ ${'jobs-finished-tab'} | ${'Finished'} | ${defaultProps.jobCounts.finished}
+ `('displays the right tab text and badge count', ({ tabId, text, count }) => {
+ expect(trimText(findTab(tabId).text())).toBe(`${text} ${count}`);
+ });
+});
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index 3d40e94d219..1432c6d7e9b 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -911,6 +911,9 @@ export const stages = [
export default {
id: 4757,
+ artifact: {
+ locked: false,
+ },
name: 'test',
build_path: '/root/ci-mock/-/jobs/4757',
retry_path: '/root/ci-mock/-/jobs/4757/retry',
@@ -955,6 +958,7 @@ export default {
artifacts: [null],
runner: {
id: 1,
+ short_sha: 'ABCDEFGH',
description: 'local ci runner',
edit_path: '/root/ci-mock/runners/1/edit',
},
@@ -1189,3 +1193,214 @@ export const jobsInStage = {
path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
};
+
+export const mockPipelineWithoutMR = {
+ id: 28029444,
+ details: {
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ path: 'pipeline/28029444',
+ ref: {
+ name: 'test-branch',
+ },
+};
+
+export const mockPipelineWithAttachedMR = {
+ id: 28029444,
+ details: {
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ path: 'pipeline/28029444',
+ flags: {
+ merge_request_pipeline: true,
+ detached_merge_request_pipeline: false,
+ },
+ merge_request: {
+ iid: 1234,
+ path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
+ title: 'Update README.md',
+ source_branch: 'feature-1234',
+ source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
+ target_branch: 'master',
+ target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
+ },
+ ref: {
+ name: 'test-branch',
+ },
+};
+
+export const mockPipelineDetached = {
+ id: 28029444,
+ details: {
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ path: 'pipeline/28029444',
+ flags: {
+ merge_request_pipeline: false,
+ detached_merge_request_pipeline: true,
+ },
+ merge_request: {
+ iid: 1234,
+ path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
+ title: 'Update README.md',
+ source_branch: 'feature-1234',
+ source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
+ target_branch: 'master',
+ target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
+ },
+ ref: {
+ name: 'test-branch',
+ },
+};
+
+export const mockJobsInTable = [
+ {
+ detailedStatus: {
+ icon: 'status_manual',
+ label: 'manual play action',
+ text: 'manual',
+ tooltip: 'manual action',
+ action: {
+ buttonTitle: 'Trigger this manual action',
+ icon: 'play',
+ method: 'post',
+ path: '/root/ci-project/-/jobs/2004/play',
+ title: 'Play',
+ __typename: 'StatusAction',
+ },
+ __typename: 'DetailedStatus',
+ },
+ id: 'gid://gitlab/Ci::Build/2004',
+ refName: 'master',
+ refPath: '/root/ci-project/-/commits/master',
+ tags: [],
+ shortSha: '2d5d8323',
+ commitPath: '/root/ci-project/-/commit/2d5d83230bdea0e003d83ef4c16d2bf9a8808ebe',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/423',
+ path: '/root/ci-project/-/pipelines/423',
+ user: {
+ webPath: '/root',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ __typename: 'User',
+ },
+ __typename: 'Pipeline',
+ },
+ stage: { name: 'test', __typename: 'CiStage' },
+ name: 'test_manual_job',
+ duration: null,
+ finishedAt: null,
+ coverage: null,
+ retryable: false,
+ playable: true,
+ cancelable: false,
+ active: false,
+ __typename: 'CiJob',
+ },
+ {
+ detailedStatus: {
+ icon: 'status_skipped',
+ label: 'skipped',
+ text: 'skipped',
+ tooltip: 'skipped',
+ action: null,
+ __typename: 'DetailedStatus',
+ },
+ id: 'gid://gitlab/Ci::Build/2021',
+ refName: 'master',
+ refPath: '/root/ci-project/-/commits/master',
+ tags: [],
+ shortSha: '2d5d8323',
+ commitPath: '/root/ci-project/-/commit/2d5d83230bdea0e003d83ef4c16d2bf9a8808ebe',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/425',
+ path: '/root/ci-project/-/pipelines/425',
+ user: {
+ webPath: '/root',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ __typename: 'User',
+ },
+ __typename: 'Pipeline',
+ },
+ stage: { name: 'test', __typename: 'CiStage' },
+ name: 'coverage_job',
+ duration: null,
+ finishedAt: null,
+ coverage: null,
+ retryable: false,
+ playable: false,
+ cancelable: false,
+ active: false,
+ __typename: 'CiJob',
+ },
+ {
+ detailedStatus: {
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ action: {
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ method: 'post',
+ path: '/root/ci-project/-/jobs/2015/retry',
+ title: 'Retry',
+ __typename: 'StatusAction',
+ },
+ __typename: 'DetailedStatus',
+ },
+ id: 'gid://gitlab/Ci::Build/2015',
+ refName: 'master',
+ refPath: '/root/ci-project/-/commits/master',
+ tags: [],
+ shortSha: '2d5d8323',
+ commitPath: '/root/ci-project/-/commit/2d5d83230bdea0e003d83ef4c16d2bf9a8808ebe',
+ pipeline: {
+ id: 'gid://gitlab/Ci::Pipeline/424',
+ path: '/root/ci-project/-/pipelines/424',
+ user: {
+ webPath: '/root',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ __typename: 'User',
+ },
+ __typename: 'Pipeline',
+ },
+ stage: { name: 'deploy', __typename: 'CiStage' },
+ name: 'artifact_job',
+ duration: 2,
+ finishedAt: '2021-04-01T17:36:18Z',
+ coverage: null,
+ retryable: true,
+ playable: false,
+ cancelable: false,
+ active: false,
+ __typename: 'CiJob',
+ },
+];