Resolve "Show failure_reason
and upgrade tooltips of jobs"
This commit is contained in:
parent
dd271e2460
commit
d54cf868f8
39 changed files with 636 additions and 39 deletions
|
@ -17,6 +17,7 @@
|
|||
* "text": "passed",
|
||||
* "label": "passed",
|
||||
* "group": "success",
|
||||
* "tooltip": "passed",
|
||||
* "details_path": "/root/ci-mock/builds/4256",
|
||||
* "action": {
|
||||
* "icon": "retry",
|
||||
|
@ -69,12 +70,12 @@
|
|||
textBuilder.push(this.job.name);
|
||||
}
|
||||
|
||||
if (this.job.name && this.status.label) {
|
||||
if (this.job.name && this.status.tooltip) {
|
||||
textBuilder.push('-');
|
||||
}
|
||||
|
||||
if (this.status.label) {
|
||||
textBuilder.push(`${this.job.status.label}`);
|
||||
if (this.status.tooltip) {
|
||||
textBuilder.push(`${this.job.status.tooltip}`);
|
||||
}
|
||||
|
||||
return textBuilder.join(' ');
|
||||
|
@ -100,6 +101,7 @@
|
|||
:title="tooltipText"
|
||||
:class="cssClassJobName"
|
||||
data-container="body"
|
||||
data-html="true"
|
||||
class="js-pipeline-graph-job-link"
|
||||
>
|
||||
|
||||
|
@ -115,6 +117,7 @@
|
|||
class="js-job-component-tooltip"
|
||||
:title="tooltipText"
|
||||
:class="cssClassJobName"
|
||||
data-html="true"
|
||||
data-container="body"
|
||||
>
|
||||
|
||||
|
|
|
@ -391,7 +391,7 @@
|
|||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $row-hover;
|
||||
background-color: $dropdown-item-hover-bg;
|
||||
}
|
||||
|
||||
.icon-retry {
|
||||
|
|
|
@ -2,7 +2,6 @@ class Projects::JobsController < Projects::ApplicationController
|
|||
include SendFileUpload
|
||||
|
||||
before_action :build, except: [:index, :cancel_all]
|
||||
|
||||
before_action :authorize_read_build!,
|
||||
only: [:index, :show, :status, :raw, :trace]
|
||||
before_action :authorize_update_build!,
|
||||
|
@ -45,8 +44,11 @@ class Projects::JobsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC')
|
||||
@builds = @builds.where("id not in (?)", @build.id)
|
||||
@builds = @project.pipelines
|
||||
.find_by_sha(@build.sha)
|
||||
.builds
|
||||
.order('id DESC')
|
||||
.present(current_user: current_user)
|
||||
@pipeline = @build.pipeline
|
||||
|
||||
respond_to do |format|
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
module Presentable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def present(attributes)
|
||||
all.map { |klass_object| klass_object.present(attributes) }
|
||||
end
|
||||
end
|
||||
|
||||
def present(**attributes)
|
||||
Gitlab::View::Presenter::Factory
|
||||
.new(self, attributes)
|
||||
|
|
|
@ -15,6 +15,8 @@ module Ci
|
|||
def status_title
|
||||
if auto_canceled?
|
||||
"Job is redundant and is auto-canceled by Pipeline ##{auto_canceled_by_id}"
|
||||
else
|
||||
tooltip_for_badge
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,5 +30,19 @@ module Ci
|
|||
trigger_request.user_variables
|
||||
end
|
||||
end
|
||||
|
||||
def tooltip_message
|
||||
"#{subject.name} - #{detailed_status.status_tooltip}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tooltip_for_badge
|
||||
detailed_status.badge_tooltip.capitalize
|
||||
end
|
||||
|
||||
def detailed_status
|
||||
@detailed_status ||= subject.detailed_status(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ class StatusEntity < Grape::Entity
|
|||
include RequestAwareEntity
|
||||
|
||||
expose :icon, :text, :label, :group
|
||||
|
||||
expose :status_tooltip, as: :tooltip
|
||||
expose :has_details?, as: :has_details
|
||||
expose :details_path
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
- css_classes = "ci-status ci-#{status.group} #{'has-tooltip' if title.present?}"
|
||||
|
||||
- if link && status.has_details?
|
||||
= link_to status.details_path, class: css_classes, title: title do
|
||||
= link_to status.details_path, class: css_classes, title: title, data: { html: title.present? } do
|
||||
= sprite_icon(status.icon)
|
||||
= status.text
|
||||
- else
|
||||
%span{ class: css_classes, title: title }
|
||||
%span{ class: css_classes, title: title, data: { html: title.present? } }
|
||||
= sprite_icon(status.icon)
|
||||
= status.text
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
- subject = local_assigns.fetch(:subject)
|
||||
- status = subject.detailed_status(current_user)
|
||||
- klass = "ci-status-icon ci-status-icon-#{status.group}"
|
||||
- tooltip = "#{subject.name} - #{status.label}"
|
||||
- tooltip = "#{subject.name} - #{status.status_tooltip}"
|
||||
|
||||
- if status.has_details?
|
||||
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
|
||||
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, html: true, container: 'body' } do
|
||||
%span{ class: klass }= sprite_icon(status.icon)
|
||||
%span.ci-build-text= subject.name
|
||||
|
||||
- else
|
||||
.menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
|
||||
.menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', html: true, title: tooltip, container: 'body' } }
|
||||
%span{ class: klass }= sprite_icon(status.icon)
|
||||
%span.ci-build-text= subject.name
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
- builds = @build.pipeline.builds.to_a
|
||||
|
||||
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
|
||||
.sidebar-container
|
||||
.blocks-container
|
||||
|
@ -91,7 +89,8 @@
|
|||
- HasStatus::ORDERED_STATUSES.each do |build_status|
|
||||
- builds.select{|build| build.status == build_status}.each do |build|
|
||||
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
|
||||
= link_to project_job_path(@project, build) do
|
||||
- tooltip = build.tooltip_message
|
||||
= link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: true, title: tooltip, container: 'body' }) do
|
||||
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
|
||||
%span{ class: "ci-status-icon-#{build.status}" }
|
||||
= ci_icon_for_status(build.status)
|
||||
|
@ -101,5 +100,4 @@
|
|||
- else
|
||||
= build.id
|
||||
- if build.retried?
|
||||
%span.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
|
||||
= sprite_icon('retry', size:16, css_class: 'icon-retry')
|
||||
= sprite_icon('retry', size:16, css_class: 'icon-retry')
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
illustration_size: 'svg-430',
|
||||
title: _('This job has not started yet'),
|
||||
content: _('This job is in pending state and is waiting to be picked by a runner')
|
||||
= render "sidebar"
|
||||
= render "sidebar", builds: @builds
|
||||
|
||||
.js-build-options{ data: javascript_build_options }
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Display error message on job's tooltip if this one fails
|
||||
merge_request: 17782
|
||||
author:
|
||||
type: added
|
|
@ -6,10 +6,12 @@ module Gitlab
|
|||
def self.extended_statuses
|
||||
[[Status::Build::Cancelable,
|
||||
Status::Build::Retryable],
|
||||
[Status::Build::Failed],
|
||||
[Status::Build::FailedAllowed,
|
||||
Status::Build::Play,
|
||||
Status::Build::Stop],
|
||||
[Status::Build::Action]]
|
||||
[Status::Build::Action],
|
||||
[Status::Build::Retried]]
|
||||
end
|
||||
|
||||
def self.common_helpers
|
||||
|
|
40
lib/gitlab/ci/status/build/failed.rb
Normal file
40
lib/gitlab/ci/status/build/failed.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
module Status
|
||||
module Build
|
||||
class Failed < Status::Extended
|
||||
REASONS = {
|
||||
'unknown_failure' => 'unknown failure',
|
||||
'script_failure' => 'script failure',
|
||||
'api_failure' => 'API failure',
|
||||
'stuck_or_timeout_failure' => 'stuck or timeout failure',
|
||||
'runner_system_failure' => 'runner system failure',
|
||||
'missing_dependency_failure' => 'missing dependency failure'
|
||||
}.freeze
|
||||
|
||||
def status_tooltip
|
||||
base_message
|
||||
end
|
||||
|
||||
def badge_tooltip
|
||||
base_message
|
||||
end
|
||||
|
||||
def self.matches?(build, user)
|
||||
build.failed?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def base_message
|
||||
"#{s_('CiStatusLabel|failed')} #{description}"
|
||||
end
|
||||
|
||||
def description
|
||||
"<br> (#{REASONS[subject.failure_reason]})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ module Gitlab
|
|||
module Build
|
||||
class FailedAllowed < Status::Extended
|
||||
def label
|
||||
'failed (allowed to fail)'
|
||||
"failed #{allowed_to_fail_title}"
|
||||
end
|
||||
|
||||
def icon
|
||||
|
@ -15,9 +15,19 @@ module Gitlab
|
|||
'failed_with_warnings'
|
||||
end
|
||||
|
||||
def status_tooltip
|
||||
"#{@status.status_tooltip} #{allowed_to_fail_title}"
|
||||
end
|
||||
|
||||
def self.matches?(build, user)
|
||||
build.failed? && build.allow_failure?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def allowed_to_fail_title
|
||||
"(allowed to fail)"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
17
lib/gitlab/ci/status/build/retried.rb
Normal file
17
lib/gitlab/ci/status/build/retried.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
module Status
|
||||
module Build
|
||||
class Retried < Status::Extended
|
||||
def status_tooltip
|
||||
@status.status_tooltip + " (retried)"
|
||||
end
|
||||
|
||||
def self.matches?(build, user)
|
||||
build.retried?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -57,6 +57,16 @@ module Gitlab
|
|||
def action_title
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Hint that appears on all the pipeline graph tooltips and builds on the right sidebar in Job detail view
|
||||
def status_tooltip
|
||||
label
|
||||
end
|
||||
|
||||
# Hint that appears on the build badges
|
||||
def badge_tooltip
|
||||
subject.status
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -238,5 +238,10 @@ FactoryBot.define do
|
|||
trait :protected do
|
||||
protected true
|
||||
end
|
||||
|
||||
trait :script_failure do
|
||||
failed
|
||||
failure_reason 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,4 +34,26 @@ describe 'User browses a job', :js do
|
|||
|
||||
expect(build.project.running_or_pending_build_count).to eq(build.project.builds.running_or_pending.count(:all))
|
||||
end
|
||||
|
||||
context 'with a failed job' do
|
||||
let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
|
||||
|
||||
it 'displays the failure reason' do
|
||||
within('.builds-container') do
|
||||
build_link = first('.build-job > a')
|
||||
expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a failed job has been retried' do
|
||||
let!(:build) { create(:ci_build, :failed, :retried, pipeline: pipeline) }
|
||||
|
||||
it 'displays the failure reason and retried label' do
|
||||
within('.builds-container') do
|
||||
build_link = first('.build-job > a')
|
||||
expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,4 +29,15 @@ describe 'User browses jobs' do
|
|||
expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a failed job' do
|
||||
let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) }
|
||||
|
||||
it 'displays a tooltip with the failure reason' do
|
||||
page.within('.ci-table') do
|
||||
failed_job_link = page.find('.ci-failed')
|
||||
expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -115,6 +115,13 @@ describe 'Pipeline', :js do
|
|||
|
||||
expect(page).not_to have_content('Retry job')
|
||||
end
|
||||
|
||||
it 'should include the failure reason' do
|
||||
page.within('#ci-badge-test') do
|
||||
build_link = page.find('.js-pipeline-graph-job-link')
|
||||
expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline has manual jobs' do
|
||||
|
@ -289,6 +296,15 @@ describe 'Pipeline', :js do
|
|||
|
||||
it { expect(build_manual.reload).to be_pending }
|
||||
end
|
||||
|
||||
context 'failed jobs' do
|
||||
it 'displays a tooltip with the failure reason' do
|
||||
page.within('.ci-table') do
|
||||
failed_job_link = page.find('.ci-failed')
|
||||
expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /:project/pipelines/:id/failures' do
|
||||
|
|
|
@ -394,6 +394,23 @@ describe 'Pipelines', :js do
|
|||
expect(build.reload).to be_canceled
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a failed pipeline' do
|
||||
let!(:build) do
|
||||
create(:ci_build, :failed, pipeline: pipeline,
|
||||
stage: 'build',
|
||||
name: 'build')
|
||||
end
|
||||
|
||||
it 'should display the failure reason' do
|
||||
find('.js-builds-dropdown-button').click
|
||||
|
||||
within('.js-builds-dropdown-list') do
|
||||
build_element = page.find('.mini-pipeline-graph-dropdown-item')
|
||||
expect(build_element['data-title']).to eq('build - failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pagination' do
|
||||
|
|
|
@ -13,6 +13,7 @@ describe('pipeline graph job component', () => {
|
|||
icon: 'icon_status_success',
|
||||
text: 'passed',
|
||||
label: 'passed',
|
||||
tooltip: 'passed',
|
||||
group: 'success',
|
||||
details_path: '/root/ci-mock/builds/4256',
|
||||
has_details: true,
|
||||
|
@ -137,6 +138,7 @@ describe('pipeline graph job component', () => {
|
|||
status: {
|
||||
icon: 'icon_status_success',
|
||||
label: 'success',
|
||||
tooltip: 'success',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -53,4 +53,14 @@ describe Gitlab::Ci::Status::Build::Action do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build, :non_playable) }
|
||||
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
|
||||
|
||||
it 'returns the status' do
|
||||
expect(subject.badge_tooltip).to eq('created')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Cancelable do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
it 'does not override status status_tooltip' do
|
||||
expect(status).to receive(:status_tooltip)
|
||||
|
||||
subject.status_tooltip
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build) }
|
||||
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
|
||||
|
||||
it 'returns the status' do
|
||||
expect(subject.badge_tooltip).to eq('pending')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'action details' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build) }
|
||||
|
|
|
@ -48,11 +48,11 @@ describe Gitlab::Ci::Status::Build::Factory do
|
|||
|
||||
it 'matches correct extended statuses' do
|
||||
expect(factory.extended_statuses)
|
||||
.to eq [Gitlab::Ci::Status::Build::Retryable]
|
||||
.to eq [Gitlab::Ci::Status::Build::Retryable, Gitlab::Ci::Status::Build::Failed]
|
||||
end
|
||||
|
||||
it 'fabricates a retryable build status' do
|
||||
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
|
||||
it 'fabricates a failed build status' do
|
||||
expect(status).to be_a Gitlab::Ci::Status::Build::Failed
|
||||
end
|
||||
|
||||
it 'fabricates status with correct details' do
|
||||
|
@ -60,6 +60,7 @@ describe Gitlab::Ci::Status::Build::Factory do
|
|||
expect(status.icon).to eq 'status_failed'
|
||||
expect(status.favicon).to eq 'favicon_status_failed'
|
||||
expect(status.label).to eq 'failed'
|
||||
expect(status.status_tooltip).to eq 'failed <br> (unknown failure)'
|
||||
expect(status).to have_details
|
||||
expect(status).to have_action
|
||||
end
|
||||
|
@ -75,6 +76,7 @@ describe Gitlab::Ci::Status::Build::Factory do
|
|||
it 'matches correct extended statuses' do
|
||||
expect(factory.extended_statuses)
|
||||
.to eq [Gitlab::Ci::Status::Build::Retryable,
|
||||
Gitlab::Ci::Status::Build::Failed,
|
||||
Gitlab::Ci::Status::Build::FailedAllowed]
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'spec_helper'
|
|||
describe Gitlab::Ci::Status::Build::FailedAllowed do
|
||||
let(:status) { double('core status') }
|
||||
let(:user) { double('user') }
|
||||
let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
|
||||
|
||||
subject do
|
||||
described_class.new(status)
|
||||
|
@ -68,6 +69,28 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
|
||||
let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
|
||||
let(:status) { described_class.new(build_status) }
|
||||
|
||||
it 'does override badge_tooltip' do
|
||||
expect(status.badge_tooltip).to eq('failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
|
||||
let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
|
||||
let(:status) { described_class.new(build_status) }
|
||||
|
||||
it 'does override status_tooltip' do
|
||||
expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.matches?' do
|
||||
subject { described_class.matches?(build, user) }
|
||||
|
||||
|
|
83
spec/lib/gitlab/ci/status/build/failed_spec.rb
Normal file
83
spec/lib/gitlab/ci/status/build/failed_spec.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Status::Build::Failed do
|
||||
let(:build) { create(:ci_build, :script_failure) }
|
||||
let(:status) { double('core status') }
|
||||
let(:user) { double('user') }
|
||||
|
||||
subject { described_class.new(status) }
|
||||
|
||||
describe '#text' do
|
||||
it 'does not override status text' do
|
||||
expect(status).to receive(:text)
|
||||
|
||||
subject.text
|
||||
end
|
||||
end
|
||||
|
||||
describe '#icon' do
|
||||
it 'does not override status icon' do
|
||||
expect(status).to receive(:icon)
|
||||
|
||||
subject.icon
|
||||
end
|
||||
end
|
||||
|
||||
describe '#group' do
|
||||
it 'does not override status group' do
|
||||
expect(status).to receive(:group)
|
||||
|
||||
subject.group
|
||||
end
|
||||
end
|
||||
|
||||
describe '#favicon' do
|
||||
it 'does not override status label' do
|
||||
expect(status).to receive(:favicon)
|
||||
|
||||
subject.favicon
|
||||
end
|
||||
end
|
||||
|
||||
describe '#label' do
|
||||
it 'does not override label' do
|
||||
expect(status).to receive(:label)
|
||||
|
||||
subject.label
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
|
||||
|
||||
it 'does override badge_tooltip' do
|
||||
expect(subject.badge_tooltip).to eq 'failed <br> (script failure)'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
|
||||
|
||||
it 'does override status_tooltip' do
|
||||
expect(subject.status_tooltip).to eq 'failed <br> (script failure)'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.matches?' do
|
||||
context 'with a failed build' do
|
||||
it 'returns true' do
|
||||
expect(described_class.matches?(build, user)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'with any other type of build' do
|
||||
let(:build) { create(:ci_build, :success) }
|
||||
|
||||
it 'returns false' do
|
||||
expect(described_class.matches?(build, user)).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,22 @@ describe Gitlab::Ci::Status::Build::Play do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
it 'does not override status status_tooltip' do
|
||||
expect(status).to receive(:status_tooltip)
|
||||
|
||||
subject.status_tooltip
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
it 'does not override status badge_tooltip' do
|
||||
expect(status).to receive(:badge_tooltip)
|
||||
|
||||
subject.badge_tooltip
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_action?' do
|
||||
context 'when user is allowed to update build' do
|
||||
context 'when user is allowed to trigger protected action' do
|
||||
|
|
96
spec/lib/gitlab/ci/status/build/retried_spec.rb
Normal file
96
spec/lib/gitlab/ci/status/build/retried_spec.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Status::Build::Retried do
|
||||
let(:build) { create(:ci_build, :retried) }
|
||||
let(:status) { double('core status') }
|
||||
let(:user) { double('user') }
|
||||
|
||||
subject { described_class.new(status) }
|
||||
|
||||
describe '#text' do
|
||||
it 'does not override status text' do
|
||||
expect(status).to receive(:text)
|
||||
|
||||
subject.text
|
||||
end
|
||||
end
|
||||
|
||||
describe '#icon' do
|
||||
it 'does not override status icon' do
|
||||
expect(status).to receive(:icon)
|
||||
|
||||
subject.icon
|
||||
end
|
||||
end
|
||||
|
||||
describe '#group' do
|
||||
it 'does not override status group' do
|
||||
expect(status).to receive(:group)
|
||||
|
||||
subject.group
|
||||
end
|
||||
end
|
||||
|
||||
describe '#favicon' do
|
||||
it 'does not override status label' do
|
||||
expect(status).to receive(:favicon)
|
||||
|
||||
subject.favicon
|
||||
end
|
||||
end
|
||||
|
||||
describe '#label' do
|
||||
it 'does not override status label' do
|
||||
expect(status).to receive(:label)
|
||||
|
||||
subject.label
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build, :retried) }
|
||||
let(:status) { Gitlab::Ci::Status::Success.new(build, user) }
|
||||
|
||||
it 'returns status' do
|
||||
expect(status.badge_tooltip).to eq('pending')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
context 'with a failed build' do
|
||||
let(:build) { create(:ci_build, :failed, :retried) }
|
||||
let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) }
|
||||
let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) }
|
||||
|
||||
it 'does override status_tooltip' do
|
||||
expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with another build' do
|
||||
let(:build) { create(:ci_build, :retried) }
|
||||
let(:status) { Gitlab::Ci::Status::Success.new(build, user) }
|
||||
|
||||
it 'does override status_tooltip' do
|
||||
expect(subject.status_tooltip).to eq 'passed (retried)'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.matches?' do
|
||||
subject { described_class.matches?(build, user) }
|
||||
|
||||
context 'with a retried build' do
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'with a build that has not been retried' do
|
||||
let(:build) { create(:ci_build, :success) }
|
||||
|
||||
it { is_expected.to be_falsy }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Retryable do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
it 'does not override status status_tooltip' do
|
||||
expect(status).to receive(:status_tooltip)
|
||||
|
||||
subject.status_tooltip
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build) }
|
||||
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
|
||||
|
||||
it 'does return status' do
|
||||
expect(status.badge_tooltip).to eq('pending')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'action details' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build) }
|
||||
|
|
|
@ -77,4 +77,24 @@ describe Gitlab::Ci::Status::Build::Stop do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status_tooltip' do
|
||||
it 'does not override status status_tooltip' do
|
||||
expect(status).to receive(:status_tooltip)
|
||||
|
||||
subject.status_tooltip
|
||||
end
|
||||
end
|
||||
|
||||
describe '#badge_tooltip' do
|
||||
let(:user) { create(:user) }
|
||||
let(:build) { create(:ci_build, :playable) }
|
||||
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
|
||||
|
||||
it 'does not override status badge_tooltip' do
|
||||
expect(status).to receive(:badge_tooltip)
|
||||
|
||||
subject.badge_tooltip
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Status::SuccessWarning do
|
||||
let(:status) { double('status') }
|
||||
|
||||
subject do
|
||||
described_class.new(double('status'))
|
||||
described_class.new(status)
|
||||
end
|
||||
|
||||
describe '#test' do
|
||||
|
|
|
@ -72,13 +72,44 @@ describe Ci::BuildPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when build is not auto-canceled' do
|
||||
before do
|
||||
expect(build).to receive(:auto_canceled?).and_return(false)
|
||||
end
|
||||
context 'when build failed' do
|
||||
let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
|
||||
|
||||
it 'does not have a status title' do
|
||||
expect(presenter.status_title).to be_nil
|
||||
it 'returns the reason of failure' do
|
||||
status_title = presenter.status_title
|
||||
|
||||
expect(status_title).to eq('Failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build has failed && retried' do
|
||||
let(:build) { create(:ci_build, :failed, :retried, pipeline: pipeline) }
|
||||
|
||||
it 'does not include retried title' do
|
||||
status_title = presenter.status_title
|
||||
|
||||
expect(status_title).not_to include('(retried)')
|
||||
expect(status_title).to eq('Failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build has failed and is allowed to' do
|
||||
let(:build) { create(:ci_build, :failed, :allowed_to_fail, pipeline: pipeline) }
|
||||
|
||||
it 'returns the reason of failure' do
|
||||
status_title = presenter.status_title
|
||||
|
||||
expect(status_title).to eq('Failed <br> (unknown failure)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'For any other build' do
|
||||
let(:build) { create(:ci_build, :success, pipeline: pipeline) }
|
||||
|
||||
it 'returns the status' do
|
||||
tooltip_description = presenter.status_title
|
||||
|
||||
expect(tooltip_description).to eq('Success')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -134,4 +165,56 @@ describe Ci::BuildPresenter do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tooltip_message' do
|
||||
context 'When build has failed' do
|
||||
let(:build) { create(:ci_build, :script_failure, pipeline: pipeline) }
|
||||
|
||||
it 'returns the reason of failure' do
|
||||
tooltip = subject.tooltip_message
|
||||
|
||||
expect(tooltip).to eq("#{build.name} - failed <br> (script failure)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'When build has failed and retried' do
|
||||
let(:build) { create(:ci_build, :script_failure, :retried, pipeline: pipeline) }
|
||||
|
||||
it 'should include the reason of failure and the retried title' do
|
||||
tooltip = subject.tooltip_message
|
||||
|
||||
expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'When build has failed and is allowed to' do
|
||||
let(:build) { create(:ci_build, :script_failure, :allowed_to_fail, pipeline: pipeline) }
|
||||
|
||||
it 'should include the reason of failure' do
|
||||
tooltip = subject.tooltip_message
|
||||
|
||||
expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'For any other build (no retried)' do
|
||||
let(:build) { create(:ci_build, :success, pipeline: pipeline) }
|
||||
|
||||
it 'should include build name and status' do
|
||||
tooltip = subject.tooltip_message
|
||||
|
||||
expect(tooltip).to eq("#{build.name} - passed")
|
||||
end
|
||||
end
|
||||
|
||||
context 'For any other build (retried)' do
|
||||
let(:build) { create(:ci_build, :success, :retried, pipeline: pipeline) }
|
||||
|
||||
it 'should include build name and status' do
|
||||
tooltip = subject.tooltip_message
|
||||
|
||||
expect(tooltip).to eq("#{build.name} - passed (retried)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,15 +28,31 @@ describe BuildSerializer do
|
|||
end
|
||||
|
||||
describe '#represent_status' do
|
||||
context 'when represents only status' do
|
||||
let(:resource) { create(:ci_build) }
|
||||
context 'for a failed build' do
|
||||
let(:resource) { create(:ci_build, :failed) }
|
||||
let(:status) { resource.detailed_status(double('user')) }
|
||||
|
||||
subject { serializer.represent_status(resource) }
|
||||
|
||||
it 'serializes only status' do
|
||||
expect(subject[:text]).to eq(status.text)
|
||||
expect(subject[:label]).to eq(status.label)
|
||||
expect(subject[:label]).to eq('failed')
|
||||
expect(subject[:tooltip]).to eq('failed <br> (unknown failure)')
|
||||
expect(subject[:icon]).to eq(status.icon)
|
||||
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for any other type of build' do
|
||||
let(:resource) { create(:ci_build, :success) }
|
||||
let(:status) { resource.detailed_status(double('user')) }
|
||||
|
||||
subject { serializer.represent_status(resource) }
|
||||
|
||||
it 'serializes only status' do
|
||||
expect(subject[:text]).to eq(status.text)
|
||||
expect(subject[:label]).to eq('passed')
|
||||
expect(subject[:tooltip]).to eq('passed')
|
||||
expect(subject[:icon]).to eq(status.icon)
|
||||
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ describe JobEntity do
|
|||
|
||||
it 'contains details' do
|
||||
expect(subject).to include :status
|
||||
expect(subject[:status]).to include :icon, :favicon, :text, :label
|
||||
expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
|
||||
end
|
||||
|
||||
context 'when job is retryable' do
|
||||
|
@ -126,7 +126,29 @@ describe JobEntity do
|
|||
|
||||
it 'contains details' do
|
||||
expect(subject).to include :status
|
||||
expect(subject[:status]).to include :icon, :favicon, :text, :label
|
||||
expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job failed' do
|
||||
let(:job) { create(:ci_build, :script_failure) }
|
||||
|
||||
describe 'status' do
|
||||
it 'should contain the failure reason inside label' do
|
||||
expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip
|
||||
expect(subject[:status][:label]).to eq('failed')
|
||||
expect(subject[:status][:tooltip]).to eq('failed <br> (script failure)')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job passed' do
|
||||
let(:job) { create(:ci_build, :success) }
|
||||
|
||||
describe 'status' do
|
||||
it 'should not contain the failure reason inside label' do
|
||||
expect(subject[:status][:label]).to eq('passed')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ describe PipelineEntity do
|
|||
expect(subject).to include :details
|
||||
expect(subject[:details])
|
||||
.to include :duration, :finished_at
|
||||
expect(subject[:details][:status]).to include :icon, :favicon, :text, :label
|
||||
expect(subject[:details][:status]).to include :icon, :favicon, :text, :label, :tooltip
|
||||
end
|
||||
|
||||
it 'contains flags' do
|
||||
|
|
|
@ -26,7 +26,7 @@ describe StageEntity do
|
|||
end
|
||||
|
||||
it 'contains detailed status' do
|
||||
expect(subject[:status]).to include :text, :label, :group, :icon
|
||||
expect(subject[:status]).to include :text, :label, :group, :icon, :tooltip
|
||||
expect(subject[:status][:label]).to eq 'passed'
|
||||
end
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ describe StatusEntity do
|
|||
subject { entity.as_json }
|
||||
|
||||
it 'contains status details' do
|
||||
expect(subject).to include :text, :icon, :favicon, :label, :group
|
||||
expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip
|
||||
expect(subject).to include :has_details, :details_path
|
||||
expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico')
|
||||
end
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'projects/jobs/show' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:build) { create(:ci_build, pipeline: pipeline) }
|
||||
let(:builds) { project.builds.present(current_user: user) }
|
||||
|
||||
let(:pipeline) do
|
||||
create(:ci_pipeline, project: project, sha: project.commit.id)
|
||||
|
@ -11,6 +13,7 @@ describe 'projects/jobs/show' do
|
|||
before do
|
||||
assign(:build, build.present)
|
||||
assign(:project, project)
|
||||
assign(:builds, builds)
|
||||
|
||||
allow(view).to receive(:can?).and_return(true)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue