Commit d54cf868 authored by Mayra Cabrera's avatar Mayra Cabrera Committed by Grzegorz Bizon

Resolve "Show `failure_reason` and upgrade tooltips of jobs"

parent dd271e24
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* "text": "passed", * "text": "passed",
* "label": "passed", * "label": "passed",
* "group": "success", * "group": "success",
* "tooltip": "passed",
* "details_path": "/root/ci-mock/builds/4256", * "details_path": "/root/ci-mock/builds/4256",
* "action": { * "action": {
* "icon": "retry", * "icon": "retry",
...@@ -69,12 +70,12 @@ ...@@ -69,12 +70,12 @@
textBuilder.push(this.job.name); textBuilder.push(this.job.name);
} }
if (this.job.name && this.status.label) { if (this.job.name && this.status.tooltip) {
textBuilder.push('-'); textBuilder.push('-');
} }
if (this.status.label) { if (this.status.tooltip) {
textBuilder.push(`${this.job.status.label}`); textBuilder.push(`${this.job.status.tooltip}`);
} }
return textBuilder.join(' '); return textBuilder.join(' ');
...@@ -100,6 +101,7 @@ ...@@ -100,6 +101,7 @@
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
data-container="body" data-container="body"
data-html="true"
class="js-pipeline-graph-job-link" class="js-pipeline-graph-job-link"
> >
...@@ -115,6 +117,7 @@ ...@@ -115,6 +117,7 @@
class="js-job-component-tooltip" class="js-job-component-tooltip"
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
data-html="true"
data-container="body" data-container="body"
> >
......
...@@ -391,7 +391,7 @@ ...@@ -391,7 +391,7 @@
} }
&:hover { &:hover {
background-color: $row-hover; background-color: $dropdown-item-hover-bg;
} }
.icon-retry { .icon-retry {
......
...@@ -2,7 +2,6 @@ class Projects::JobsController < Projects::ApplicationController ...@@ -2,7 +2,6 @@ class Projects::JobsController < Projects::ApplicationController
include SendFileUpload include SendFileUpload
before_action :build, except: [:index, :cancel_all] before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!, before_action :authorize_read_build!,
only: [:index, :show, :status, :raw, :trace] only: [:index, :show, :status, :raw, :trace]
before_action :authorize_update_build!, before_action :authorize_update_build!,
...@@ -45,8 +44,11 @@ class Projects::JobsController < Projects::ApplicationController ...@@ -45,8 +44,11 @@ class Projects::JobsController < Projects::ApplicationController
end end
def show def show
@builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC') @builds = @project.pipelines
@builds = @builds.where("id not in (?)", @build.id) .find_by_sha(@build.sha)
.builds
.order('id DESC')
.present(current_user: current_user)
@pipeline = @build.pipeline @pipeline = @build.pipeline
respond_to do |format| respond_to do |format|
......
module Presentable module Presentable
extend ActiveSupport::Concern
class_methods do
def present(attributes)
all.map { |klass_object| klass_object.present(attributes) }
end
end
def present(**attributes) def present(**attributes)
Gitlab::View::Presenter::Factory Gitlab::View::Presenter::Factory
.new(self, attributes) .new(self, attributes)
......
...@@ -15,6 +15,8 @@ module Ci ...@@ -15,6 +15,8 @@ module Ci
def status_title def status_title
if auto_canceled? if auto_canceled?
"Job is redundant and is auto-canceled by Pipeline ##{auto_canceled_by_id}" "Job is redundant and is auto-canceled by Pipeline ##{auto_canceled_by_id}"
else
tooltip_for_badge
end end
end end
...@@ -28,5 +30,19 @@ module Ci ...@@ -28,5 +30,19 @@ module Ci
trigger_request.user_variables trigger_request.user_variables
end end
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
end end
...@@ -2,7 +2,7 @@ class StatusEntity < Grape::Entity ...@@ -2,7 +2,7 @@ class StatusEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :icon, :text, :label, :group expose :icon, :text, :label, :group
expose :status_tooltip, as: :tooltip
expose :has_details?, as: :has_details expose :has_details?, as: :has_details
expose :details_path expose :details_path
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
- css_classes = "ci-status ci-#{status.group} #{'has-tooltip' if title.present?}" - css_classes = "ci-status ci-#{status.group} #{'has-tooltip' if title.present?}"
- if link && status.has_details? - 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) = sprite_icon(status.icon)
= status.text = status.text
- else - else
%span{ class: css_classes, title: title } %span{ class: css_classes, title: title, data: { html: title.present? } }
= sprite_icon(status.icon) = sprite_icon(status.icon)
= status.text = status.text
...@@ -3,14 +3,15 @@ ...@@ -3,14 +3,15 @@
- subject = local_assigns.fetch(:subject) - subject = local_assigns.fetch(:subject)
- status = subject.detailed_status(current_user) - status = subject.detailed_status(current_user)
- klass = "ci-status-icon ci-status-icon-#{status.group}" - klass = "ci-status-icon ci-status-icon-#{status.group}"
- tooltip = "#{subject.name} - #{status.label}" - tooltip = "#{subject.name} - #{status.status_tooltip}"
- if status.has_details? - 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{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name %span.ci-build-text= subject.name
- else - 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{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name %span.ci-build-text= subject.name
......
- 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" } } %aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
.sidebar-container .sidebar-container
.blocks-container .blocks-container
...@@ -91,7 +89,8 @@ ...@@ -91,7 +89,8 @@
- HasStatus::ORDERED_STATUSES.each do |build_status| - HasStatus::ORDERED_STATUSES.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build| - builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } .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') = sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" } %span{ class: "ci-status-icon-#{build.status}" }
= ci_icon_for_status(build.status) = ci_icon_for_status(build.status)
...@@ -101,5 +100,4 @@ ...@@ -101,5 +100,4 @@
- else - else
= build.id = build.id
- if build.retried? - 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 @@ ...@@ -107,7 +107,7 @@
illustration_size: 'svg-430', illustration_size: 'svg-430',
title: _('This job has not started yet'), title: _('This job has not started yet'),
content: _('This job is in pending state and is waiting to be picked by a runner') 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 } .js-build-options{ data: javascript_build_options }
......
---
title: Display error message on job's tooltip if this one fails
merge_request: 17782
author:
type: added
...@@ -6,10 +6,12 @@ module Gitlab ...@@ -6,10 +6,12 @@ module Gitlab
def self.extended_statuses def self.extended_statuses
[[Status::Build::Cancelable, [[Status::Build::Cancelable,
Status::Build::Retryable], Status::Build::Retryable],
[Status::Build::Failed],
[Status::Build::FailedAllowed, [Status::Build::FailedAllowed,
Status::Build::Play, Status::Build::Play,
Status::Build::Stop], Status::Build::Stop],
[Status::Build::Action]] [Status::Build::Action],
[Status::Build::Retried]]
end end
def self.common_helpers def self.common_helpers
......
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 ...@@ -4,7 +4,7 @@ module Gitlab
module Build module Build
class FailedAllowed < Status::Extended class FailedAllowed < Status::Extended
def label def label
'failed (allowed to fail)' "failed #{allowed_to_fail_title}"
end end
def icon def icon
...@@ -15,9 +15,19 @@ module Gitlab ...@@ -15,9 +15,19 @@ module Gitlab
'failed_with_warnings' 'failed_with_warnings'
end end
def status_tooltip
"#{@status.status_tooltip} #{allowed_to_fail_title}"
end
def self.matches?(build, user) def self.matches?(build, user)
build.failed? && build.allow_failure? build.failed? && build.allow_failure?
end end
private
def allowed_to_fail_title
"(allowed to fail)"
end
end end
end end
end end
......
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 ...@@ -57,6 +57,16 @@ module Gitlab
def action_title def action_title
raise NotImplementedError raise NotImplementedError
end 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 end
end end
......
...@@ -238,5 +238,10 @@ FactoryBot.define do ...@@ -238,5 +238,10 @@ FactoryBot.define do
trait :protected do trait :protected do
protected true protected true
end end
trait :script_failure do
failed
failure_reason 1
end
end end
end end
...@@ -34,4 +34,26 @@ describe 'User browses a job', :js do ...@@ -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)) expect(build.project.running_or_pending_build_count).to eq(build.project.builds.running_or_pending.count(:all))
end 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 end
...@@ -29,4 +29,15 @@ describe 'User browses jobs' do ...@@ -29,4 +29,15 @@ describe 'User browses jobs' do
expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path) expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path)
end end
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 end
...@@ -115,6 +115,13 @@ describe 'Pipeline', :js do ...@@ -115,6 +115,13 @@ describe 'Pipeline', :js do
expect(page).not_to have_content('Retry job') expect(page).not_to have_content('Retry job')
end 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 end
context 'when pipeline has manual jobs' do context 'when pipeline has manual jobs' do
...@@ -289,6 +296,15 @@ describe 'Pipeline', :js do ...@@ -289,6 +296,15 @@ describe 'Pipeline', :js do
it { expect(build_manual.reload).to be_pending } it { expect(build_manual.reload).to be_pending }
end 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 end
describe 'GET /:project/pipelines/:id/failures' do describe 'GET /:project/pipelines/:id/failures' do
......
...@@ -394,6 +394,23 @@ describe 'Pipelines', :js do ...@@ -394,6 +394,23 @@ describe 'Pipelines', :js do
expect(build.reload).to be_canceled expect(build.reload).to be_canceled
end end
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 end
context 'with pagination' do context 'with pagination' do
......
...@@ -13,6 +13,7 @@ describe('pipeline graph job component', () => { ...@@ -13,6 +13,7 @@ describe('pipeline graph job component', () => {
icon: 'icon_status_success', icon: 'icon_status_success',
text: 'passed', text: 'passed',
label: 'passed', label: 'passed',
tooltip: 'passed',
group: 'success', group: 'success',
details_path: '/root/ci-mock/builds/4256', details_path: '/root/ci-mock/builds/4256',
has_details: true, has_details: true,
...@@ -137,6 +138,7 @@ describe('pipeline graph job component', () => { ...@@ -137,6 +138,7 @@ describe('pipeline graph job component', () => {
status: { status: {
icon: 'icon_status_success', icon: 'icon_status_success',
label: 'success', label: 'success',
tooltip: 'success',
}, },
}, },
}); });
......
...@@ -53,4 +53,14 @@ describe Gitlab::Ci::Status::Build::Action do ...@@ -53,4 +53,14 @@ describe Gitlab::Ci::Status::Build::Action do
end end
end 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 end
...@@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Cancelable do ...@@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Cancelable do
end 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) }
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 describe 'action details' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:build) { create(:ci_build) } let(:build) { create(:ci_build) }
......
...@@ -48,11 +48,11 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -48,11 +48,11 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'matches correct extended statuses' do it 'matches correct extended statuses' do
expect(factory.extended_statuses) expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable] .to eq [Gitlab::Ci::Status::Build::Retryable, Gitlab::Ci::Status::Build::Failed]
end end
it 'fabricates a retryable build status' do it 'fabricates a failed build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable expect(status).to be_a Gitlab::Ci::Status::Build::Failed
end end
it 'fabricates status with correct details' do it 'fabricates status with correct details' do
...@@ -60,6 +60,7 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -60,6 +60,7 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.icon).to eq 'status_failed' expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed' expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq '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_details
expect(status).to have_action expect(status).to have_action
end end
...@@ -75,6 +76,7 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -75,6 +76,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'matches correct extended statuses' do it 'matches correct extended statuses' do
expect(factory.extended_statuses) expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable, .to eq [Gitlab::Ci::Status::Build::Retryable,
Gitlab::Ci::Status::Build::Failed,
Gitlab::Ci::Status::Build::FailedAllowed] Gitlab::Ci::Status::Build::FailedAllowed]
end end
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Build::FailedAllowed do describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { double('core status') } let(:status) { double('core status') }
let(:user) { double('user') } let(:user) { double('user') }
let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
subject do subject do
described_class.new(status) described_class.new(status)
...@@ -68,6 +69,28 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do ...@@ -68,6 +69,28 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
end end
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 describe '.matches?' do
subject { described_class.matches?(build, user) } subject { described_class.matches?(build, user) }
......
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 ...@@ -14,6 +14,22 @@ describe Gitlab::Ci::Status::Build::Play do
end 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
it 'does not override status badge_tooltip' do
expect(status).to receive(:badge_tooltip)
subject.badge_tooltip
end
end
describe '#has_action?' do describe '#has_action?' do
context 'when user is allowed to update build' do context 'when user is allowed to update build' do
context 'when user is allowed to trigger protected action' do context 'when user is allowed to trigger protected action' do
......
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 ...@@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Retryable do
end 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) }
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 describe 'action details' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:build) { create(:ci_build) } let(:build) { create(:ci_build) }
......
...@@ -77,4 +77,24 @@ describe Gitlab::Ci::Status::Build::Stop do ...@@ -77,4 +77,24 @@ describe Gitlab::Ci::Status::Build::Stop do
end end
end 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 end
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Status::SuccessWarning do describe Gitlab::Ci::Status::SuccessWarning do
let(:status) { double('status') }
subject do subject do
described_class.new(double('status')) described_class.new(status)
end end
describe '#test' do describe '#test' do
......
...@@ -72,13 +72,44 @@ describe Ci::BuildPresenter do ...@@ -72,13 +72,44 @@ describe Ci::BuildPresenter do
end end
end end
context 'when build is not auto-canceled' do context 'when build failed' do
before do let(:build) { create(:ci_build, :failed, pipeline: pipeline) }
expect(build).to receive(:auto_canceled?).and_return(false)
it 'returns the reason of failure' do
status_title = presenter.status_title
expect(status_title).to eq('Failed <br> (unknown failure)')
end
end end
it 'does not have a status title' do context 'when build has failed && retried' do
expect(presenter.status_title).to be_nil 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 end
end end
...@@ -134,4 +165,56 @@ describe Ci::BuildPresenter do ...@@ -134,4 +165,56 @@ describe Ci::BuildPresenter do
end end
end 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 end
...@@ -28,15 +28,31 @@ describe BuildSerializer do ...@@ -28,15 +28,31 @@ describe BuildSerializer do
end end
describe '#represent_status' do describe '#represent_status' do
context 'when represents only status' do context 'for a failed build' do
let(:resource) { create(:ci_build) } 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('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')) } let(:status) { resource.detailed_status(double('user')) }
subject { serializer.represent_status(resource) } subject { serializer.represent_status(resource) }
it 'serializes only status' do it 'serializes only status' do
expect(subject[:text]).to eq(status.text) expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq(status.label) expect(subject[:label]).to eq('passed')
expect(subject[:tooltip]).to eq('passed')
expect(subject[:icon]).to eq(status.icon) expect(subject[:icon]).to eq(status.icon)
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
end end
......
...@@ -38,7 +38,7 @@ describe JobEntity do ...@@ -38,7 +38,7 @@ describe JobEntity do
it 'contains details' do it 'contains details' do
expect(subject).to include :status 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 is retryable' do context 'when job is retryable' do
...@@ -126,7 +126,29 @@ describe JobEntity do ...@@ -126,7 +126,29 @@ describe JobEntity do
it 'contains details' do it 'contains details' do
expect(subject).to include :status 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 end
end end
...@@ -30,7 +30,7 @@ describe PipelineEntity do ...@@ -30,7 +30,7 @@ describe PipelineEntity do
expect(subject).to include :details expect(subject).to include :details
expect(subject[:details]) expect(subject[:details])
.to include :duration, :finished_at .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 end
it 'contains flags' do it 'contains flags' do
......
...@@ -26,7 +26,7 @@ describe StageEntity do ...@@ -26,7 +26,7 @@ describe StageEntity do
end end
it 'contains detailed status' do 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' expect(subject[:status][:label]).to eq 'passed'
end end
......
...@@ -16,7 +16,7 @@ describe StatusEntity do ...@@ -16,7 +16,7 @@ describe StatusEntity do
subject { entity.as_json } subject { entity.as_json }
it 'contains status details' do 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).to include :has_details, :details_path
expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico') expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico')
end end
......
require 'spec_helper' require 'spec_helper'
describe 'projects/jobs/show' do describe 'projects/jobs/show' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:build) { create(:ci_build, pipeline: pipeline) } let(:build) { create(:ci_build, pipeline: pipeline) }
let(:builds) { project.builds.present(current_user: user) }
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, project: project, sha: project.commit.id) create(:ci_pipeline, project: project, sha: project.commit.id)
...@@ -11,6 +13,7 @@ describe 'projects/jobs/show' do ...@@ -11,6 +13,7 @@ describe 'projects/jobs/show' do
before do before do
assign(:build, build.present) assign(:build, build.present)
assign(:project, project) assign(:project, project)
assign(:builds, builds)
allow(view).to receive(:can?).and_return(true) allow(view).to receive(:can?).and_return(true)
end end
......
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