Skip to content
Snippets Groups Projects
Commit dadc046d authored by Alessio Caiazza's avatar Alessio Caiazza Committed by Nick Thomas
Browse files

post merge pipeline and environments status

parent 289651e2
No related branches found
No related tags found
No related merge requests found
Showing
with 552 additions and 117 deletions
Loading
Loading
@@ -2,6 +2,7 @@
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import FilteredSearchDropdown from '~/vue_shared/components/filtered_search_dropdown.vue';
import { __ } from '~/locale';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import tooltip from '../../vue_shared/directives/tooltip';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
Loading
Loading
@@ -31,6 +32,11 @@ export default {
required: true,
},
},
deployedTextMap: {
running: __('Deploying to'),
success: __('Deployed to'),
failed: __('Failed to deploy to'),
},
data() {
const features = window.gon.features || {};
return {
Loading
Loading
@@ -54,10 +60,13 @@ export default {
hasMetrics() {
return !!this.deployment.metrics_url;
},
deployedText() {
return this.$options.deployedTextMap[this.deployment.status];
},
},
methods: {
stopEnvironment() {
const msg = 'Are you sure you want to stop this environment?';
const msg = __('Are you sure you want to stop this environment?');
const isConfirmed = confirm(msg); // eslint-disable-line
 
if (isConfirmed) {
Loading
Loading
@@ -87,10 +96,10 @@ export default {
<div class="ci-widget media">
<div class="media-body">
<div class="deploy-body">
<div class="deployment-info">
<div class="js-deployment-info deployment-info">
<template v-if="hasDeploymentMeta">
<span>
Deployed to
{{ deployedText }}
</span>
<tooltip-on-truncate
:title="deployment.name"
Loading
Loading
<script>
import _ from 'underscore';
import { __ } from '~/locale';
import Project from '~/pages/projects/project';
import SmartInterval from '~/smart_interval';
import createFlash from '../flash';
Loading
Loading
@@ -80,6 +82,7 @@ export default {
const service = this.createService(store);
return {
mr: store,
state: store.state,
service,
};
},
Loading
Loading
@@ -103,6 +106,17 @@ export default {
(!this.mr.isNothingToMergeState && !this.mr.isMergedState)
);
},
shouldRenderMergedPipeline() {
return this.mr.state === 'merged' && !_.isEmpty(this.mr.mergePipeline);
},
},
watch: {
state(newVal, oldVal) {
if (newVal !== oldVal && this.shouldRenderMergedPipeline) {
// init polling
this.initPostMergeDeploymentsPolling();
}
}
},
created() {
this.initPolling();
Loading
Loading
@@ -112,11 +126,19 @@ export default {
mounted() {
this.setFaviconHelper();
this.initDeploymentsPolling();
if (this.shouldRenderMergedPipeline) {
this.initPostMergeDeploymentsPolling();
}
},
beforeDestroy() {
eventHub.$off('mr.discussion.updated', this.checkStatus);
this.pollingInterval.destroy();
this.deploymentsInterval.destroy();
if (this.postMergeDeploymentsInterval) {
this.postMergeDeploymentsInterval.destroy();
}
},
methods: {
createService(store) {
Loading
Loading
@@ -146,7 +168,13 @@ export default {
cb.call(null, data);
}
})
.catch(() => createFlash('Something went wrong. Please try again.'));
.catch(() => createFlash(__('Something went wrong. Please try again.')));
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
return Promise.resolve();
},
initPolling() {
this.pollingInterval = new SmartInterval({
Loading
Loading
@@ -158,8 +186,14 @@ export default {
});
},
initDeploymentsPolling() {
this.deploymentsInterval = new SmartInterval({
callback: this.fetchDeployments,
this.deploymentsInterval = this.deploymentsPoll(this.fetchPreMergeDeployments);
},
initPostMergeDeploymentsPolling() {
this.postMergeDeploymentsInterval = this.deploymentsPoll(this.fetchPostMergeDeployments);
},
deploymentsPoll(callback) {
return new SmartInterval({
callback,
startingInterval: 30000,
maxInterval: 120000,
hiddenInterval: 240000,
Loading
Loading
@@ -167,26 +201,29 @@ export default {
immediateExecution: true,
});
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
return Promise.resolve();
fetchDeployments(target) {
return this.service.fetchDeployments(target);
},
fetchDeployments() {
return this.service
.fetchDeployments()
.then(res => res.data)
.then(data => {
fetchPreMergeDeployments() {
return this.fetchDeployments()
.then(({ data }) => {
if (data.length) {
this.mr.deployments = data;
}
})
.catch(() => {
createFlash(
'Something went wrong while fetching the environments for this merge request. Please try again.',
);
});
.catch(() => this.throwDeploymentsError());
},
fetchPostMergeDeployments(){
return this.fetchDeployments('merge_commit')
.then(({ data }) => {
if (data.length) {
this.mr.postMergeDeployments = data;
}
})
.catch(() => this.throwDeploymentsError());
},
throwDeploymentsError() {
createFlash(__('Something went wrong while fetching the environments for this merge request. Please try again.'));
},
fetchActionsContent() {
this.service
Loading
Loading
@@ -199,7 +236,7 @@ export default {
Project.initRefSwitcher();
}
})
.catch(() => createFlash('Something went wrong. Please try again.'));
.catch(() => createFlash(__('Something went wrong. Please try again.')));
},
handleNotification(data) {
if (data.ci_status === this.mr.ciStatus) return;
Loading
Loading
@@ -267,7 +304,8 @@ export default {
/>
<deployment
v-for="deployment in mr.deployments"
:key="deployment.id"
:key="`pre-merge-deploy-${deployment.id}`"
class="js-pre-merge-deploy"
:deployment="deployment"
/>
<div class="mr-section-container">
Loading
Loading
@@ -308,5 +346,22 @@ export default {
<mr-widget-merge-help />
</div>
</div>
<template v-if="shouldRenderMergedPipeline">
<mr-widget-pipeline
class="js-post-merge-pipeline prepend-top-default"
:pipeline="mr.mergePipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch="mr.targetBranch"
:source-branch-link="mr.targetBranch"
/>
<deployment
v-for="postMergeDeployment in mr.postMergeDeployments"
:key="`post-merge-deploy-${postMergeDeployment.id}`"
:deployment="postMergeDeployment"
class="js-post-deployment"
/>
</template>
</div>
</template>
Loading
Loading
@@ -21,8 +21,12 @@ export default class MRWidgetService {
return axios.delete(this.endpoints.sourceBranchPath);
}
 
fetchDeployments() {
return axios.get(this.endpoints.ciEnvironmentsStatusPath);
fetchDeployments(targetParam) {
return axios.get(this.endpoints.ciEnvironmentsStatusPath, {
params: {
environment_target: targetParam
}
});
}
 
poll() {
Loading
Loading
Loading
Loading
@@ -32,7 +32,9 @@ export default class MergeRequestStore {
this.commitsCount = data.commits_count;
this.divergedCommitsCount = data.diverged_commits_count;
this.pipeline = data.pipeline || {};
this.mergePipeline = data.merge_pipeline || {};
this.deployments = this.deployments || data.deployments || [];
this.postMergeDeployments = this.postMergeDeployments || [];
this.initRebase(data);
 
if (data.issues_links) {
Loading
Loading
Loading
Loading
@@ -201,9 +201,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
 
def ci_environments_status
environments = @merge_request.environments_for(current_user).map do |environment|
EnvironmentStatus.new(environment, @merge_request)
end
environments = if ci_environments_status_on_merge_result?
EnvironmentStatus.after_merge_request(@merge_request, current_user)
else
EnvironmentStatus.for_merge_request(@merge_request, current_user)
end
 
render json: EnvironmentStatusSerializer.new(current_user: current_user).represent(environments)
end
Loading
Loading
@@ -241,6 +243,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
 
private
 
def ci_environments_status_on_merge_result?
params[:environment_target] == 'merge_commit'
end
def target_branch_missing?
@merge_request.has_no_commits? && !@merge_request.target_branch_exists?
end
Loading
Loading
Loading
Loading
@@ -127,6 +127,10 @@ class Deployment < ActiveRecord::Base
metrics&.merge(deployment_time: created_at.to_i) || {}
end
 
def status
'success'
end
private
 
def prometheus_adapter
Loading
Loading
Loading
Loading
@@ -3,21 +3,33 @@
class EnvironmentStatus
include Gitlab::Utils::StrongMemoize
 
attr_reader :environment, :merge_request
attr_reader :environment, :merge_request, :sha
 
delegate :id, to: :environment
delegate :name, to: :environment
delegate :project, to: :environment
delegate :deployed_at, to: :deployment, allow_nil: true
delegate :status, to: :deployment
 
def initialize(environment, merge_request)
def self.for_merge_request(mr, user)
build_environments_status(mr, user, mr.head_pipeline)
end
def self.after_merge_request(mr, user)
return [] unless mr.merged?
build_environments_status(mr, user, mr.merge_pipeline)
end
def initialize(environment, merge_request, sha)
@environment = environment
@merge_request = merge_request
@sha = sha
end
 
def deployment
strong_memoize(:deployment) do
environment.first_deployment_for(merge_request.diff_head_sha)
environment.first_deployment_for(sha)
end
end
 
Loading
Loading
@@ -26,10 +38,9 @@ class EnvironmentStatus
end
 
def changes
sha = merge_request.diff_head_sha
return [] if project.route_map_for(sha).nil?
 
changed_files.map { |file| build_change(file, sha) }.compact
changed_files.map { |file| build_change(file) }.compact
end
 
def changed_files
Loading
Loading
@@ -41,7 +52,7 @@ class EnvironmentStatus
 
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
 
def build_change(file, sha)
def build_change(file)
public_path = project.public_path_for_source_path(file.new_path, sha)
return if public_path.nil?
 
Loading
Loading
@@ -53,4 +64,22 @@ class EnvironmentStatus
external_url: environment.external_url_for(file.new_path, sha)
}
end
def self.build_environments_status(mr, user, pipeline)
return [] unless pipeline.present?
find_environments(user, pipeline).map do |environment|
EnvironmentStatus.new(environment, mr, pipeline.sha)
end
end
private_class_method :build_environments_status
def self.find_environments(user, pipeline)
env_ids = Deployment.where(deployable: pipeline.builds).select(:environment_id)
Environment.available.where(id: env_ids).select do |environment|
Ability.allowed?(user, :read_environment, environment)
end
end
private_class_method :find_environments
end
Loading
Loading
@@ -204,6 +204,12 @@ class MergeRequest < ActiveRecord::Base
head_pipeline&.sha == diff_head_sha ? head_pipeline : nil
end
 
def merge_pipeline
return unless merged?
target_project.pipeline_for(target_branch, merge_commit_sha)
end
# Pattern used to extract `!123` merge request references from text
#
# This pattern supports cross-project references.
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ class EnvironmentStatusEntity < Grape::Entity
 
expose :id
expose :name
expose :status
 
expose :url do |es|
project_environment_path(es.project, es.environment)
Loading
Loading
Loading
Loading
@@ -55,6 +55,7 @@ class MergeRequestWidgetEntity < IssuableEntity
 
expose :merge_commit_message
expose :actual_head_pipeline, with: PipelineDetailsEntity, as: :pipeline
expose :merge_pipeline, with: PipelineDetailsEntity, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)}
 
# Booleans
expose :merge_ongoing?, as: :merge_ongoing
Loading
Loading
---
title: Show post-merge pipeline in merge request page
merge_request: 22292
author:
type: added
Loading
Loading
@@ -648,6 +648,9 @@ msgstr ""
msgid "Are you sure you want to reset the health check token?"
msgstr ""
 
msgid "Are you sure you want to stop this environment?"
msgstr ""
msgid "Are you sure?"
msgstr ""
 
Loading
Loading
@@ -2324,6 +2327,12 @@ msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
msgstr ""
 
msgid "Deployed to"
msgstr ""
msgid "Deploying to"
msgstr ""
msgid "Deprioritize label"
msgstr ""
 
Loading
Loading
@@ -2750,6 +2759,9 @@ msgstr ""
msgid "Failed to check related branches."
msgstr ""
 
msgid "Failed to deploy to"
msgstr ""
msgid "Failed to load emoji list."
msgstr ""
 
Loading
Loading
@@ -5604,6 +5616,9 @@ msgstr ""
msgid "Something went wrong while fetching comments. Please try again."
msgstr ""
 
msgid "Something went wrong while fetching the environments for this merge request. Please try again."
msgstr ""
msgid "Something went wrong while fetching the projects."
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -749,13 +749,15 @@ describe Projects::MergeRequestsController do
 
describe 'GET ci_environments_status' do
context 'the environment is from a forked project' do
let!(:forked) { fork_project(project, user, repository: true) }
let!(:environment) { create(:environment, project: forked) }
let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
let(:admin) { create(:admin) }
let(:forked) { fork_project(project, user, repository: true) }
let(:sha) { forked.commit.sha }
let(:environment) { create(:environment, project: forked) }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: forked) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let!(:deployment) { create(:deployment, environment: environment, sha: sha, ref: 'master', deployable: build) }
 
let(:merge_request) do
create(:merge_request, source_project: forked, target_project: project)
create(:merge_request, source_project: forked, target_project: project, target_branch: 'master', head_pipeline: pipeline)
end
 
it 'links to the environment on that project' do
Loading
Loading
@@ -764,6 +766,35 @@ describe Projects::MergeRequestsController do
expect(json_response.first['url']).to match /#{forked.full_path}/
end
 
context "when environment_target is 'merge_commit'" do
it 'returns nothing' do
get_ci_environments_status(environment_target: 'merge_commit')
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
context 'when is merged' do
let(:source_environment) { create(:environment, project: project) }
let(:merge_commit_sha) { project.repository.merge(user, forked.commit.id, merge_request, "merged in test") }
let(:post_merge_pipeline) { create(:ci_pipeline, sha: merge_commit_sha, project: project) }
let(:post_merge_build) { create(:ci_build, pipeline: post_merge_pipeline) }
let!(:source_deployment) { create(:deployment, environment: source_environment, sha: merge_commit_sha, ref: 'master', deployable: post_merge_build) }
before do
merge_request.update!(merge_commit_sha: merge_commit_sha)
merge_request.mark_as_merged!
end
it 'returns the enviroment on the source project' do
get_ci_environments_status(environment_target: 'merge_commit')
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.first['url']).to match /#{project.full_path}/
end
end
end
# we're trying to reduce the overall number of queries for this method.
# set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-ce/issues/52287
it 'keeps queries in check' do
Loading
Loading
@@ -772,11 +803,15 @@ describe Projects::MergeRequestsController do
expect(control_count).to be <= 137
end
 
def get_ci_environments_status
get :ci_environments_status,
def get_ci_environments_status(extra_params = {})
params = {
namespace_id: merge_request.project.namespace.to_param,
project_id: merge_request.project,
id: merge_request.iid, format: 'json'
id: merge_request.iid,
format: 'json'
}
get :ci_environments_status, params.merge(extra_params)
end
end
end
Loading
Loading
Loading
Loading
@@ -3,15 +3,19 @@ require 'rails_helper'
describe 'Merge request > User sees deployment widget', :js do
describe 'when deployed to an environment' do
let(:user) { create(:user) }
let(:project) { merge_request.target_project }
let(:merge_request) { create(:merge_request, :merged) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, :merged, source_project: project) }
let(:environment) { create(:environment, project: project) }
let(:role) { :developer }
let(:sha) { project.commit('master').id }
let!(:deployment) { create(:deployment, environment: environment, sha: sha) }
let(:ref) { merge_request.target_branch }
let(:sha) { project.commit(ref).id }
let(:pipeline) { create(:ci_pipeline_without_jobs, sha: sha, project: project, ref: ref) }
let(:build) { create(:ci_build, :success, pipeline: pipeline) }
let!(:deployment) { create(:deployment, environment: environment, sha: sha, ref: ref, deployable: build) }
let!(:manual) { }
 
before do
merge_request.update!(merge_commit_sha: sha)
project.add_user(user, role)
sign_in(user)
visit project_merge_request_path(project, merge_request)
Loading
Loading
@@ -26,15 +30,10 @@ describe 'Merge request > User sees deployment widget', :js do
end
 
context 'with stop action' do
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
let(:deployment) do
create(:deployment, environment: environment, ref: merge_request.target_branch,
sha: sha, deployable: build, on_stop: 'close_app')
end
 
before do
deployment.update!(on_stop: manual.name)
wait_for_requests
end
 
Loading
Loading
Loading
Loading
@@ -40,21 +40,26 @@ describe 'Merge request > User sees merge widget', :js do
 
context 'view merge request' do
let!(:environment) { create(:environment, project: project) }
let(:sha) { project.commit(merge_request.source_branch).sha }
let(:pipeline) { create(:ci_pipeline_without_jobs, status: 'success', sha: sha, project: project, ref: merge_request.source_branch) }
let(:build) { create(:ci_build, :success, pipeline: pipeline) }
 
let!(:deployment) do
create(:deployment, environment: environment,
ref: 'feature',
sha: merge_request.diff_head_sha)
ref: merge_request.source_branch,
deployable: build,
sha: sha)
end
 
before do
merge_request.update!(head_pipeline: pipeline)
visit project_merge_request_path(project, merge_request)
end
 
it 'shows environments link' do
wait_for_requests
 
page.within('.mr-widget-heading') do
page.within('.js-pre-merge-deploy') do
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url)
end
Loading
Loading
Loading
Loading
@@ -46,6 +46,7 @@
"diff_head_commit_short_id": { "type": ["string", "null"] },
"merge_commit_message": { "type": ["string", "null"] },
"pipeline": { "type": ["object", "null"] },
"merge_pipeline": { "type": ["object", "null"] },
"work_in_progress": { "type": "boolean" },
"source_branch_exists": { "type": "boolean" },
"mergeable_discussions_state": { "type": "boolean" },
Loading
Loading
Loading
Loading
@@ -2,54 +2,48 @@ import Vue from 'vue';
import deploymentComponent from '~/vue_merge_request_widget/components/deployment.vue';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import { getTimeago } from '~/lib/utils/datetime_utility';
import mountComponent from '../../helpers/vue_mount_component_helper';
 
const deploymentMockData = {
id: 15,
name: 'review/diplo',
url: '/root/acets-review-apps/environments/15',
stop_url: '/root/acets-review-apps/environments/15/stop',
metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
external_url: 'http://diplo.',
external_url_formatted: 'diplo.',
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
changes: [
{
path: 'index.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
},
{
path: 'imgs/gallery.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
},
{
path: 'about/',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
},
],
};
const createComponent = () => {
describe('Deployment component', () => {
const Component = Vue.extend(deploymentComponent);
const deploymentMockData = {
id: 15,
name: 'review/diplo',
url: '/root/review-apps/environments/15',
stop_url: '/root/review-apps/environments/15/stop',
metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
external_url: 'http://gitlab.com.',
external_url_formatted: 'gitlab',
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
changes: [
{
path: 'index.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
},
{
path: 'imgs/gallery.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
},
{
path: 'about/',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
},
],
};
 
return new Component({
el: document.createElement('div'),
propsData: { deployment: { ...deploymentMockData } },
});
};
describe('Deployment component', () => {
let vm;
 
beforeEach(() => {
vm = createComponent();
});
afterEach(() => {
vm.$destroy();
});
 
describe('computed', () => {
describe('', () => {
beforeEach(() => {
vm = mountComponent(Component, { deployment: { ...deploymentMockData } });
});
describe('deployTimeago', () => {
it('return formatted date', () => {
const readable = getTimeago().format(deploymentMockData.deployed_at);
Loading
Loading
@@ -111,9 +105,7 @@ describe('Deployment component', () => {
expect(vm.hasDeploymentMeta).toEqual(false);
});
});
});
 
describe('methods', () => {
describe('stopEnvironment', () => {
const url = '/foo/bar';
const returnPromise = () =>
Loading
Loading
@@ -152,42 +144,33 @@ describe('Deployment component', () => {
expect(MRWidgetService.stopEnvironment).not.toHaveBeenCalled();
});
});
});
describe('template', () => {
let el;
beforeEach(() => {
vm = createComponent(deploymentMockData);
el = vm.$el;
});
 
it('renders deployment name', () => {
expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(
expect(vm.$el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(
deploymentMockData.url,
);
 
expect(el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name);
expect(vm.$el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name);
});
 
it('renders external URL', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(
expect(vm.$el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(
deploymentMockData.external_url,
);
 
expect(el.querySelector('.js-deploy-url').innerText).toContain('View app');
expect(vm.$el.querySelector('.js-deploy-url').innerText).toContain('View app');
});
 
it('renders stop button', () => {
expect(el.querySelector('.btn')).not.toBeNull();
expect(vm.$el.querySelector('.btn')).not.toBeNull();
});
 
it('renders deployment time', () => {
expect(el.querySelector('.js-deploy-time').innerText).toContain(vm.deployTimeago);
expect(vm.$el.querySelector('.js-deploy-time').innerText).toContain(vm.deployTimeago);
});
 
it('renders metrics component', () => {
expect(el.querySelector('.js-mr-memory-usage')).not.toBeNull();
expect(vm.$el.querySelector('.js-mr-memory-usage')).not.toBeNull();
});
});
 
Loading
Loading
@@ -196,8 +179,7 @@ describe('Deployment component', () => {
window.gon = window.gon || {};
window.gon.features = window.gon.features || {};
window.gon.features.ciEnvironmentsStatusChanges = true;
vm = createComponent(deploymentMockData);
vm = mountComponent(Component, { deployment: { ...deploymentMockData } });
});
 
afterEach(() => {
Loading
Loading
@@ -216,7 +198,7 @@ describe('Deployment component', () => {
window.gon.features = window.gon.features || {};
window.gon.features.ciEnvironmentsStatusChanges = false;
 
vm = createComponent(deploymentMockData);
vm = mountComponent(Component, { deployment: { ...deploymentMockData } });
});
 
afterEach(() => {
Loading
Loading
@@ -228,4 +210,44 @@ describe('Deployment component', () => {
expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).not.toBeNull();
});
});
describe('deployment status', () => {
describe('running', () => {
beforeEach(() => {
vm = mountComponent(Component, {
deployment: Object.assign({}, deploymentMockData, { status: 'running' }),
});
});
it('renders information about running deployment', () => {
expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain('Deploying to');
});
});
describe('success', () => {
beforeEach(() => {
vm = mountComponent(Component, {
deployment: Object.assign({}, deploymentMockData, { status: 'success' }),
});
});
it('renders information about finished deployment', () => {
expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain('Deployed to');
});
});
describe('failed', () => {
beforeEach(() => {
vm = mountComponent(Component, {
deployment: Object.assign({}, deploymentMockData, { status: 'failed' }),
});
});
it('renders information about finished deployment', () => {
expect(vm.$el.querySelector('.js-deployment-info').textContent).toContain(
'Failed to deploy to',
);
});
});
});
});
Loading
Loading
@@ -189,7 +189,7 @@ describe('mrWidgetOptions', () => {
it('should fetch deployments', done => {
spyOn(vm.service, 'fetchDeployments').and.returnValue(returnPromise([{ id: 1 }]));
 
vm.fetchDeployments();
vm.fetchPreMergeDeployments();
 
setTimeout(() => {
expect(vm.service.fetchDeployments).toHaveBeenCalled();
Loading
Loading
@@ -454,6 +454,7 @@ describe('mrWidgetOptions', () => {
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
changes,
status: 'success'
};
 
beforeEach(done => {
Loading
Loading
@@ -486,4 +487,189 @@ describe('mrWidgetOptions', () => {
).toEqual(changes.length);
});
});
describe('pipeline for target branch after merge', () => {
describe('with information for target branch pipeline', () => {
beforeEach(done => {
vm.mr.state = 'merged';
vm.mr.mergePipeline = {
id: 127,
user: {
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: null,
web_url: 'http://localhost:3000/root',
status_tooltip_html: null,
path: '/root',
},
active: true,
coverage: null,
source: 'push',
created_at: '2018-10-22T11:41:35.186Z',
updated_at: '2018-10-22T11:41:35.433Z',
path: '/root/ci-web-terminal/pipelines/127',
flags: {
latest: true,
stuck: true,
auto_devops: false,
yaml_errors: false,
retryable: false,
cancelable: true,
failure_reason: false,
},
details: {
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/root/ci-web-terminal/pipelines/127',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
},
duration: null,
finished_at: null,
stages: [
{
name: 'test',
title: 'test: pending',
status: {
icon: 'status_pending',
text: 'pending',
label: 'pending',
group: 'pending',
tooltip: 'pending',
has_details: true,
details_path: '/root/ci-web-terminal/pipelines/127#test',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png',
},
path: '/root/ci-web-terminal/pipelines/127#test',
dropdown_path: '/root/ci-web-terminal/pipelines/127/stage.json?stage=test',
},
],
artifacts: [],
manual_actions: [],
scheduled_actions: [],
},
ref: {
name: 'master',
path: '/root/ci-web-terminal/commits/master',
tag: false,
branch: true,
},
commit: {
id: 'aa1939133d373c94879becb79d91828a892ee319',
short_id: 'aa193913',
title: "Merge branch 'master-test' into 'master'",
created_at: '2018-10-22T11:41:33.000Z',
parent_ids: [
'4622f4dd792468993003caf2e3be978798cbe096',
'76598df914cdfe87132d0c3c40f80db9fa9396a4',
],
message:
"Merge branch 'master-test' into 'master'\n\nUpdate .gitlab-ci.yml\n\nSee merge request root/ci-web-terminal!1",
author_name: 'Administrator',
author_email: 'admin@example.com',
authored_date: '2018-10-22T11:41:33.000Z',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
committed_date: '2018-10-22T11:41:33.000Z',
author: {
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: null,
web_url: 'http://localhost:3000/root',
status_tooltip_html: null,
path: '/root',
},
author_gravatar_url: null,
commit_url:
'http://localhost:3000/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
commit_path: '/root/ci-web-terminal/commit/aa1939133d373c94879becb79d91828a892ee319',
},
cancel_path: '/root/ci-web-terminal/pipelines/127/cancel',
};
vm.$nextTick(done);
});
it('renders pipeline block', () => {
expect(vm.$el.querySelector('.js-post-merge-pipeline')).not.toBeNull();
});
describe('with post merge deployments', () => {
beforeEach(done => {
vm.mr.postMergeDeployments = [{
id: 15,
name: 'review/diplo',
url: '/root/acets-review-apps/environments/15',
stop_url: '/root/acets-review-apps/environments/15/stop',
metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
external_url: 'http://diplo.',
external_url_formatted: 'diplo.',
deployed_at: '2017-03-22T22:44:42.258Z',
deployed_at_formatted: 'Mar 22, 2017 10:44pm',
changes: [
{
path: 'index.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
},
{
path: 'imgs/gallery.html',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
},
{
path: 'about/',
external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
},
],
status: 'success'
}];
vm.$nextTick(done);
});
it('renders post deployment information', () => {
expect(vm.$el.querySelector('.js-post-deployment')).not.toBeNull();
});
});
});
describe('without information for target branch pipeline', () => {
beforeEach(done => {
vm.mr.state = 'merged';
vm.$nextTick(done);
});
it('does not render pipeline block', () => {
expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull();
});
});
describe('when state is not merged', () => {
beforeEach(done => {
vm.mr.state = 'archived';
vm.$nextTick(done);
});
it('does not render pipeline block', () => {
expect(vm.$el.querySelector('.js-post-merge-pipeline')).toBeNull();
});
it('does not render post deployment information', () => {
expect(vm.$el.querySelector('.js-post-deployment')).toBeNull();
});
});
});
});
Loading
Loading
@@ -5,13 +5,15 @@ describe EnvironmentStatus do
let(:environment) { deployment.environment}
let(:project) { deployment.project }
let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) }
let(:sha) { deployment.sha }
 
subject(:environment_status) { described_class.new(environment, merge_request) }
subject(:environment_status) { described_class.new(environment, merge_request, sha) }
 
it { is_expected.to delegate_method(:id).to(:environment) }
it { is_expected.to delegate_method(:name).to(:environment) }
it { is_expected.to delegate_method(:project).to(:environment) }
it { is_expected.to delegate_method(:deployed_at).to(:deployment).as(:created_at) }
it { is_expected.to delegate_method(:status).to(:deployment) }
 
describe '#project' do
subject { environment_status.project }
Loading
Loading
@@ -58,4 +60,32 @@ describe EnvironmentStatus do
)
end
end
describe '.for_merge_request' do
let(:admin) { create(:admin) }
let(:pipeline) { create(:ci_pipeline, sha: sha) }
it 'is based on merge_request.head_pipeline' do
expect(merge_request).to receive(:head_pipeline).and_return(pipeline)
expect(merge_request).not_to receive(:merge_pipeline)
described_class.for_merge_request(merge_request, admin)
end
end
describe '.after_merge_request' do
let(:admin) { create(:admin) }
let(:pipeline) { create(:ci_pipeline, sha: sha) }
before do
merge_request.mark_as_merged!
end
it 'is based on merge_request.merge_pipeline' do
expect(merge_request).to receive(:merge_pipeline).and_return(pipeline)
expect(merge_request).not_to receive(:head_pipeline)
described_class.after_merge_request(merge_request, admin)
end
end
end
Loading
Loading
@@ -1058,6 +1058,26 @@ describe MergeRequest do
end
end
 
describe '#merge_pipeline' do
it 'returns nil when not merged' do
expect(subject.merge_pipeline).to be_nil
end
context 'when the MR is merged' do
let(:sha) { subject.target_project.commit.id }
let(:pipeline) { create(:ci_empty_pipeline, sha: sha, ref: subject.target_branch, project: subject.target_project) }
before do
subject.mark_as_merged!
subject.update_attribute(:merge_commit_sha, pipeline.sha)
end
it 'returns the post-merge pipeline' do
expect(subject.merge_pipeline).to eq(pipeline)
end
end
end
describe '#has_ci?' do
let(:merge_request) { build_stubbed(:merge_request) }
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment