Commit 6fe0739b authored by Nathan Friend's avatar Nathan Friend

Update job detail sidebar for post-merge pipelines

This commit updates the job detail view sidebar to accommodate new
states introduced as part of the post-merge pipeline feature.
parent dd43abec
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { GlLink } from '@gitlab/ui';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -7,6 +8,7 @@ export default { ...@@ -7,6 +8,7 @@ export default {
components: { components: {
CiIcon, CiIcon,
Icon, Icon,
GlLink,
}, },
props: { props: {
pipeline: { pipeline: {
...@@ -26,6 +28,12 @@ export default { ...@@ -26,6 +28,12 @@ export default {
hasRef() { hasRef() {
return !_.isEmpty(this.pipeline.ref); return !_.isEmpty(this.pipeline.ref);
}, },
isTriggeredByMergeRequest() {
return Boolean(this.pipeline.merge_request);
},
isMergeRequestPipeline() {
return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
},
}, },
methods: { methods: {
onStageClick(stage) { onStageClick(stage) {
...@@ -36,16 +44,41 @@ export default { ...@@ -36,16 +44,41 @@ export default {
</script> </script>
<template> <template>
<div class="block-last dropdown"> <div class="block-last dropdown">
<ci-icon :status="pipeline.details.status" class="vertical-align-middle" /> <div class="js-pipeline-info">
<ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
<span class="font-weight-bold">{{ __('Pipeline') }}</span> <span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span>
<a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path" <gl-link :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
>#{{ pipeline.id }}</a >#{{ pipeline.id }}</gl-link
> >
<template v-if="hasRef"> <template v-if="hasRef">
{{ __('from') }} {{ s__('Job|for') }}
<a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a>
</template> <template v-if="isTriggeredByMergeRequest">
<gl-link :href="pipeline.merge_request.path" class="link-commit ref-name js-mr-link"
>!{{ pipeline.merge_request.iid }}</gl-link
>
{{ s__('Job|with') }}
<gl-link
:href="pipeline.merge_request.source_branch_path"
class="link-commit ref-name js-source-branch-link"
>{{ pipeline.merge_request.source_branch }}</gl-link
>
<template v-if="isMergeRequestPipeline">
{{ s__('Job|into') }}
<gl-link
:href="pipeline.merge_request.target_branch_path"
class="link-commit ref-name js-target-branch-link"
>{{ pipeline.merge_request.target_branch }}</gl-link
>
</template>
</template>
<gl-link v-else :href="pipeline.ref.path" class="link-commit ref-name">{{
pipeline.ref.name
}}</gl-link>
</template>
</div>
<button <button
type="button" type="button"
......
---
title: Update job detail sidebar to accommodate post-merge pipeline information
merge_request: 25777
author:
type: added
...@@ -4424,6 +4424,9 @@ msgstr "" ...@@ -4424,6 +4424,9 @@ msgstr ""
msgid "Job|Keep" msgid "Job|Keep"
msgstr "" msgstr ""
msgid "Job|Pipeline"
msgstr ""
msgid "Job|Scroll to bottom" msgid "Job|Scroll to bottom"
msgstr "" msgstr ""
...@@ -4442,6 +4445,15 @@ msgstr "" ...@@ -4442,6 +4445,15 @@ msgstr ""
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it." msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
msgstr "" msgstr ""
msgid "Job|for"
msgstr ""
msgid "Job|into"
msgstr ""
msgid "Job|with"
msgstr ""
msgid "Jul" msgid "Jul"
msgstr "" msgstr ""
......
...@@ -2,6 +2,9 @@ require 'spec_helper' ...@@ -2,6 +2,9 @@ require 'spec_helper'
require 'tempfile' require 'tempfile'
describe 'Jobs', :clean_gitlab_redis_shared_state do describe 'Jobs', :clean_gitlab_redis_shared_state do
include Gitlab::Routing
include ProjectForksHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user_access_level) { :developer } let(:user_access_level) { :developer }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
...@@ -121,6 +124,112 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -121,6 +124,112 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end end
end end
context 'pipeline info block', :js do
it 'shows pipeline id and source branch' do
visit project_job_path(project, job)
within '.js-pipeline-info' do
expect(page).to have_content("Pipeline ##{pipeline.id} for #{pipeline.ref}")
end
end
context 'when pipeline is detached merge request pipeline' do
let(:merge_request) do
create(:merge_request,
:with_detached_merge_request_pipeline,
target_project: target_project,
source_project: source_project)
end
let(:source_project) { project }
let(:target_project) { project }
let(:pipeline) { merge_request.all_pipelines.last }
let(:job) { create(:ci_build, pipeline: pipeline) }
it 'shows merge request iid and source branch' do
visit project_job_path(project, job)
within '.js-pipeline-info' do
expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
"with #{pipeline.merge_request.source_branch}")
expect(page).to have_link("!#{pipeline.merge_request.iid}",
href: project_merge_request_path(project, merge_request))
expect(page).to have_link(pipeline.merge_request.source_branch,
href: project_commits_path(project, merge_request.source_branch))
end
end
context 'when source project is a forked project' do
let(:source_project) { fork_project(project, user, repository: true) }
let(:target_project) { project }
it 'shows merge request iid and source branch' do
visit project_job_path(source_project, job)
within '.js-pipeline-info' do
expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
"with #{pipeline.merge_request.source_branch}")
expect(page).to have_link("!#{pipeline.merge_request.iid}",
href: project_merge_request_path(project, merge_request))
expect(page).to have_link(pipeline.merge_request.source_branch,
href: project_commits_path(source_project, merge_request.source_branch))
end
end
end
end
context 'when pipeline is merge request pipeline' do
let(:merge_request) do
create(:merge_request,
:with_merge_request_pipeline,
target_project: target_project,
source_project: source_project)
end
let(:source_project) { project }
let(:target_project) { project }
let(:pipeline) { merge_request.all_pipelines.last }
let(:job) { create(:ci_build, pipeline: pipeline) }
it 'shows merge request iid and source branch' do
visit project_job_path(project, job)
within '.js-pipeline-info' do
expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
"with #{pipeline.merge_request.source_branch} " \
"into #{pipeline.merge_request.target_branch}")
expect(page).to have_link("!#{pipeline.merge_request.iid}",
href: project_merge_request_path(project, merge_request))
expect(page).to have_link(pipeline.merge_request.source_branch,
href: project_commits_path(project, merge_request.source_branch))
expect(page).to have_link(pipeline.merge_request.target_branch,
href: project_commits_path(project, merge_request.target_branch))
end
end
context 'when source project is a forked project' do
let(:source_project) { fork_project(project, user, repository: true) }
let(:target_project) { project }
it 'shows merge request iid and source branch' do
visit project_job_path(source_project, job)
within '.js-pipeline-info' do
expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
"with #{pipeline.merge_request.source_branch} " \
"into #{pipeline.merge_request.target_branch}")
expect(page).to have_link("!#{pipeline.merge_request.iid}",
href: project_merge_request_path(project, merge_request))
expect(page).to have_link(pipeline.merge_request.source_branch,
href: project_commits_path(source_project, merge_request.source_branch))
expect(page).to have_link(pipeline.merge_request.target_branch,
href: project_commits_path(project, merge_request.target_branch))
end
end
end
end
end
context 'sidebar', :js do context 'sidebar', :js do
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') } let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
......
import Vue from 'vue'; import Vue from 'vue';
import component from '~/jobs/components/stages_dropdown.vue'; import component from '~/jobs/components/stages_dropdown.vue';
import { trimText } from 'spec/helpers/vue_component_helper';
import mountComponent from '../../helpers/vue_mount_component_helper'; import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Stages Dropdown', () => { describe('Stages Dropdown', () => {
const Component = Vue.extend(component); const Component = Vue.extend(component);
let vm; let vm;
beforeEach(() => { const mockPipelineData = {
vm = mountComponent(Component, { id: 28029444,
pipeline: { details: {
id: 28029444, status: {
details: { details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
status: { group: 'success',
details_path: '/gitlab-org/gitlab-ce/pipelines/28029444', has_details: true,
group: 'success', icon: 'status_success',
has_details: true, label: 'passed',
icon: 'status_success', text: 'passed',
label: 'passed', tooltip: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
}, },
stages: [ },
{ path: 'pipeline/28029444',
name: 'build', flags: {
}, merge_request_pipeline: true,
{ detached_merge_request_pipeline: false,
name: 'test', },
}, merge_request: {
], iid: 1234,
selectedStage: 'deploy', 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',
},
};
describe('without a merge request pipeline', () => {
let 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,
stages: [{ name: 'build' }, { name: 'test' }],
selectedStage: 'deploy',
});
}); });
});
afterEach(() => { afterEach(() => {
vm.$destroy(); vm.$destroy();
}); });
it('renders pipeline status', () => { it('renders pipeline status', () => {
expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull(); expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
}); });
it('renders pipeline link', () => {
expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
'pipeline/28029444',
);
});
it('renders dropdown with stages', () => {
expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
});
it('rendes selected stage', () => {
expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
});
it('renders pipeline link', () => { it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => {
expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual( const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`;
'pipeline/28029444', const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
);
expect(actual).toBe(expected);
});
}); });
it('renders dropdown with stages', () => { describe('with an "attached" merge request pipeline', () => {
expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build'); 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,
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);
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);
});
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);
});
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);
});
}); });
it('rendes selected stage', () => { describe('with a detached merge request pipeline', () => {
expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy'); 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,
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);
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);
});
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);
});
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment