Update merge request widget pipeline block

This commit updates the pipeline block that appears on the merge request
page to include information that will be exposed by the API as part of
the post-merge pipeline feature.
This commit is contained in:
Nathan Friend 2019-03-21 15:31:09 +00:00 committed by Filipa Lacerda
parent 1e8cd7f9d9
commit 7e6b57499f
11 changed files with 328 additions and 18 deletions

View File

@ -1,5 +1,6 @@
<script>
/* eslint-disable vue/require-default-prop */
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { sprintf, __ } from '~/locale';
import PipelineStage from '~/pipelines/components/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
@ -14,9 +15,13 @@ export default {
CiIcon,
Icon,
TooltipOnTruncate,
GlLink,
LinkedPipelinesMiniList: () =>
import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [mrWidgetPipelineMixin],
props: {
pipeline: {
@ -78,12 +83,18 @@ export default {
false,
);
},
isTriggeredByMergeRequest() {
return Boolean(this.pipeline.merge_request);
},
isMergeRequestPipeline() {
return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
},
},
};
</script>
<template>
<div v-if="hasPipeline || hasCIError" class="ci-widget media">
<div v-if="hasPipeline || hasCIError" class="ci-widget media js-ci-widget">
<template v-if="hasCIError">
<div
class="add-border ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-default"
@ -99,21 +110,58 @@ export default {
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
<div class="media-body">
<div class="font-weight-bold">
Pipeline
<a :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
>#{{ pipeline.id }}</a
<div class="font-weight-bold js-pipeline-info-container">
{{ s__('Pipeline|Pipeline') }}
<gl-link :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
>#{{ pipeline.id }}</gl-link
>
{{ pipeline.details.status.label }}
<template v-if="hasCommitInfo">
for
<a
{{ s__('Pipeline|for') }}
<gl-link
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link font-weight-normal"
>{{ pipeline.commit.short_id }}</a
>{{ pipeline.commit.short_id }}</gl-link
>
on
{{ s__('Pipeline|on') }}
<template v-if="isTriggeredByMergeRequest">
<gl-link
v-gl-tooltip
:href="pipeline.merge_request.path"
:title="pipeline.merge_request.title"
class="font-weight-normal"
>!{{ pipeline.merge_request.iid }}</gl-link
>
{{ s__('Pipeline|with') }}
<tooltip-on-truncate
:title="pipeline.merge_request.source_branch"
truncate-target="child"
class="label-branch label-truncate"
>
<gl-link
:href="pipeline.merge_request.source_branch_path"
class="font-weight-normal"
>{{ pipeline.merge_request.source_branch }}</gl-link
>
</tooltip-on-truncate>
<template v-if="isMergeRequestPipeline">
{{ s__('Pipeline|into') }}
<tooltip-on-truncate
:title="pipeline.merge_request.target_branch"
truncate-target="child"
class="label-branch label-truncate"
>
<gl-link
:href="pipeline.merge_request.target_branch_path"
class="font-weight-normal"
>{{ pipeline.merge_request.target_branch }}</gl-link
>
</tooltip-on-truncate>
</template>
</template>
<tooltip-on-truncate
v-else
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate"
@ -121,7 +169,9 @@ export default {
/>
</template>
</div>
<div v-if="pipeline.coverage" class="coverage">Coverage {{ pipeline.coverage }}%</div>
<div v-if="pipeline.coverage" class="coverage">
{{ s__('Pipeline|Coverage') }} {{ pipeline.coverage }}%
</div>
</div>
</div>
<div>

View File

@ -746,6 +746,10 @@ module Ci
triggered_by_merge_request? && target_sha == merge_request.target_branch_sha
end
def matches_sha_or_source_sha?(sha)
self.sha == sha || self.source_sha == sha
end
private
def ci_yaml_from_repo

View File

@ -232,7 +232,7 @@ class MergeRequest < ActiveRecord::Base
# branch head commit, for example checking if a merge request can be merged.
# For more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/40004
def actual_head_pipeline
head_pipeline&.sha == diff_head_sha ? head_pipeline : nil
head_pipeline&.matches_sha_or_source_sha?(diff_head_sha) ? head_pipeline : nil
end
def merge_pipeline

View File

@ -0,0 +1,6 @@
---
title: Update pipeline block on merge request page to accommodate post-merge pipeline
information
merge_request: 25745
author:
type: added

View File

@ -5791,6 +5791,9 @@ msgstr ""
msgid "Pipeline|Commit"
msgstr ""
msgid "Pipeline|Coverage"
msgstr ""
msgid "Pipeline|Create for"
msgstr ""
@ -5836,9 +5839,21 @@ msgstr ""
msgid "Pipeline|all"
msgstr ""
msgid "Pipeline|for"
msgstr ""
msgid "Pipeline|into"
msgstr ""
msgid "Pipeline|on"
msgstr ""
msgid "Pipeline|success"
msgstr ""
msgid "Pipeline|with"
msgstr ""
msgid "Pipeline|with stage"
msgstr ""

View File

@ -145,6 +145,119 @@ describe 'Merge request > User sees merge widget', :js do
end
end
context 'when merge request has a branch pipeline as the head pipeline' do
let!(:pipeline) do
create(:ci_pipeline,
ref: merge_request.source_branch,
sha: merge_request.source_branch_sha,
project: merge_request.source_project)
end
before do
merge_request.update_head_pipeline
visit project_merge_request_path(project, merge_request)
end
it 'shows head pipeline information' do
within '.ci-widget-content' do
expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
"for #{pipeline.short_sha} " \
"on #{pipeline.ref}")
end
end
end
context 'when merge request has a detached merge request pipeline as the head pipeline' do
let(:merge_request) do
create(:merge_request,
:with_detached_merge_request_pipeline,
source_project: source_project,
target_project: target_project)
end
let!(:pipeline) do
merge_request.all_pipelines.last
end
let(:source_project) { project }
let(:target_project) { project }
before do
merge_request.update_head_pipeline
visit project_merge_request_path(project, merge_request)
end
it 'shows head pipeline information' do
within '.ci-widget-content' do
expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
"for #{pipeline.short_sha} " \
"on #{merge_request.to_reference} " \
"with #{merge_request.source_branch}")
end
end
context 'when source project is a forked project' do
let(:source_project) { fork_project(project, user, repository: true) }
it 'shows head pipeline information' do
within '.ci-widget-content' do
expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
"for #{pipeline.short_sha} " \
"on #{merge_request.to_reference} " \
"with #{merge_request.source_branch}")
end
end
end
end
context 'when merge request has a merge request pipeline as the head pipeline' do
let(:merge_request) do
create(:merge_request,
:with_merge_request_pipeline,
source_project: source_project,
target_project: target_project,
merge_sha: merge_sha)
end
let!(:pipeline) do
merge_request.all_pipelines.last
end
let(:source_project) { project }
let(:target_project) { project }
let(:merge_sha) { project.commit.sha }
before do
merge_request.update_head_pipeline
visit project_merge_request_path(project, merge_request)
end
it 'shows head pipeline information' do
within '.ci-widget-content' do
expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
"for #{pipeline.short_sha} " \
"on #{merge_request.to_reference} " \
"with #{merge_request.source_branch} " \
"into #{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(:merge_sha) { source_project.commit.sha }
it 'shows head pipeline information' do
within '.ci-widget-content' do
expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
"for #{pipeline.short_sha} " \
"on #{merge_request.to_reference} " \
"with #{merge_request.source_branch} " \
"into #{merge_request.target_branch}")
end
end
end
end
context 'view merge request with MWBS button' do
before do
commit_status = create(:commit_status, project: project, status: 'pending')

View File

@ -1,6 +1,7 @@
import Vue from 'vue';
import pipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { trimText } from 'spec/helpers/vue_component_helper';
import mockData from '../mock_data';
describe('MRWidgetPipeline', () => {
@ -123,7 +124,7 @@ describe('MRWidgetPipeline', () => {
describe('without commit path', () => {
beforeEach(() => {
const mockCopy = Object.assign({}, mockData);
const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.commit;
vm = mountComponent(Component, {
@ -164,7 +165,7 @@ describe('MRWidgetPipeline', () => {
describe('without coverage', () => {
it('should not render a coverage', () => {
const mockCopy = Object.assign({}, mockData);
const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.coverage;
vm = mountComponent(Component, {
@ -180,7 +181,7 @@ describe('MRWidgetPipeline', () => {
describe('without a pipeline graph', () => {
it('should not render a pipeline graph', () => {
const mockCopy = Object.assign({}, mockData);
const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.details.stages;
vm = mountComponent(Component, {
@ -193,5 +194,81 @@ describe('MRWidgetPipeline', () => {
expect(vm.$el.querySelector('.js-mini-pipeline-graph')).toEqual(null);
});
});
describe('without pipeline.merge_request', () => {
it('should render info that includes the commit and branch details', () => {
const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.merge_request;
const { pipeline } = mockCopy;
vm = mountComponent(Component, {
pipeline,
hasCi: true,
ciStatus: 'success',
troubleshootingDocsPath: 'help',
sourceBranchLink: mockCopy.source_branch_link,
});
const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
pipeline.commit.short_id
} on ${mockCopy.source_branch_link}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText);
expect(actual).toBe(expected);
});
});
describe('with pipeline.merge_request and flags.merge_request_pipeline', () => {
it('should render info that includes the commit, MR, source branch, and target branch details', () => {
const mockCopy = JSON.parse(JSON.stringify(mockData));
const { pipeline } = mockCopy;
pipeline.flags.merge_request_pipeline = true;
pipeline.flags.detached_merge_request_pipeline = false;
vm = mountComponent(Component, {
pipeline,
hasCi: true,
ciStatus: 'success',
troubleshootingDocsPath: 'help',
sourceBranchLink: mockCopy.source_branch_link,
});
const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
pipeline.commit.short_id
} on !${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-container').innerText);
expect(actual).toBe(expected);
});
});
describe('with pipeline.merge_request and flags.detached_merge_request_pipeline', () => {
it('should render info that includes the commit, MR, and source branch details', () => {
const mockCopy = JSON.parse(JSON.stringify(mockData));
const { pipeline } = mockCopy;
pipeline.flags.merge_request_pipeline = false;
pipeline.flags.detached_merge_request_pipeline = true;
vm = mountComponent(Component, {
pipeline,
hasCi: true,
ciStatus: 'success',
troubleshootingDocsPath: 'help',
sourceBranchLink: mockCopy.source_branch_link,
});
const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
pipeline.commit.short_id
} on !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText);
expect(actual).toBe(expected);
});
});
});
});

View File

@ -134,6 +134,8 @@ export default {
yaml_errors: false,
retryable: true,
cancelable: false,
merge_request_pipeline: false,
detached_merge_request_pipeline: true,
},
ref: {
name: 'daaaa',
@ -141,6 +143,15 @@ export default {
tag: false,
branch: true,
},
merge_request: {
iid: 1,
path: '/root/detached-merge-request-pipelines/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
commit: {
id: '104096c51715e12e7ae41f9333e9fa35b73f385d',
short_id: '104096c5',

View File

@ -362,6 +362,30 @@ describe Ci::Pipeline, :mailer do
end
end
describe '#matches_sha_or_source_sha?' do
subject { pipeline.matches_sha_or_source_sha?(sample_sha) }
let(:sample_sha) { Digest::SHA1.hexdigest(SecureRandom.hex) }
context 'when sha matches' do
let(:pipeline) { build(:ci_pipeline, sha: sample_sha) }
it { is_expected.to be_truthy }
end
context 'when source_sha matches' do
let(:pipeline) { build(:ci_pipeline, source_sha: sample_sha) }
it { is_expected.to be_truthy }
end
context 'when both sha and source_sha do not matche' do
let(:pipeline) { build(:ci_pipeline, sha: 'test', source_sha: 'test') }
it { is_expected.to be_falsy }
end
end
describe '.triggered_for_branch' do
subject { described_class.triggered_for_branch(ref) }

View File

@ -64,8 +64,8 @@ describe EnvironmentStatus do
end
describe '.for_merge_request' do
let(:admin) { create(:admin) }
let(:pipeline) { create(:ci_pipeline, sha: sha) }
let(:admin) { create(:admin) }
let!(:pipeline) { create(:ci_pipeline, sha: sha, merge_requests_as_head_pipeline: [merge_request]) }
it 'is based on merge_request.diff_head_sha' do
expect(merge_request).to receive(:diff_head_sha)

View File

@ -1187,8 +1187,10 @@ describe MergeRequest do
end
context 'head pipeline' do
let(:diff_head_sha) { Digest::SHA1.hexdigest(SecureRandom.hex) }
before do
allow(subject).to receive(:diff_head_sha).and_return('lastsha')
allow(subject).to receive(:diff_head_sha).and_return(diff_head_sha)
end
describe '#head_pipeline' do
@ -1216,7 +1218,15 @@ describe MergeRequest do
end
it 'returns the pipeline for MR with recent pipeline' do
pipeline = create(:ci_empty_pipeline, sha: 'lastsha')
pipeline = create(:ci_empty_pipeline, sha: diff_head_sha)
subject.update_attribute(:head_pipeline_id, pipeline.id)
expect(subject.actual_head_pipeline).to eq(subject.head_pipeline)
expect(subject.actual_head_pipeline).to eq(pipeline)
end
it 'returns the pipeline for MR with recent merge request pipeline' do
pipeline = create(:ci_empty_pipeline, sha: 'merge-sha', source_sha: diff_head_sha)
subject.update_attribute(:head_pipeline_id, pipeline.id)
expect(subject.actual_head_pipeline).to eq(subject.head_pipeline)