Commit 7446c2f5 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'feature/success-warning-icons-in-stages-builds' into 'master'

Use a warning icon for a stage with allowed to fail builds

Closes #21948

See merge request !8503
parents 1a8647f5 86217866
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
} }
.ci-status-icon-pending, .ci-status-icon-pending,
.ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings { .ci-status-icon-success_with_warnings {
color: $gl-warning; color: $gl-warning;
......
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
overflow: visible; overflow: visible;
} }
&.ci-failed { &.ci-failed,
&.ci-failed_with_warnings {
color: $gl-danger; color: $gl-danger;
border-color: $gl-danger; border-color: $gl-danger;
......
...@@ -128,16 +128,21 @@ module Ci ...@@ -128,16 +128,21 @@ module Ci
end end
def stages def stages
# TODO, this needs refactoring, see gitlab-ce#26481.
stages_query = statuses
.group('stage').select(:stage).order('max(stage_idx)')
status_sql = statuses.latest.where('stage=sg.stage').status_sql status_sql = statuses.latest.where('stage=sg.stage').status_sql
stages_query = statuses.group('stage').select(:stage) warnings_sql = statuses.latest.select('COUNT(*) > 0')
.order('max(stage_idx)') .where('stage=sg.stage').failed_but_allowed.to_sql
stages_with_statuses = CommitStatus.from(stages_query, :sg). stages_with_statuses = CommitStatus.from(stages_query, :sg)
pluck('sg.stage', status_sql) .pluck('sg.stage', status_sql, "(#{warnings_sql})")
stages_with_statuses.map do |stage| stages_with_statuses.map do |stage|
Ci::Stage.new(self, name: stage.first, status: stage.last) Ci::Stage.new(self, Hash[%i[name status warnings].zip(stage)])
end end
end end
......
...@@ -8,10 +8,11 @@ module Ci ...@@ -8,10 +8,11 @@ module Ci
delegate :project, to: :pipeline delegate :project, to: :pipeline
def initialize(pipeline, name:, status: nil) def initialize(pipeline, name:, status: nil, warnings: nil)
@pipeline = pipeline @pipeline = pipeline
@name = name @name = name
@status = status @status = status
@warnings = warnings
end end
def to_param def to_param
...@@ -39,5 +40,17 @@ module Ci ...@@ -39,5 +40,17 @@ module Ci
def builds def builds
@builds ||= pipeline.builds.where(stage: name) @builds ||= pipeline.builds.where(stage: name)
end end
def success?
status.to_s == 'success'
end
def has_warnings?
if @warnings.nil?
statuses.latest.failed_but_allowed.any?
else
@warnings
end
end
end end
end end
module HasStatus module HasStatus
extend ActiveSupport::Concern extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped] AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]
STARTED_STATUSES = %w[running success failed skipped] STARTED_STATUSES = %w[running success failed skipped]
ACTIVE_STATUSES = %w[pending running] ACTIVE_STATUSES = %w[pending running]
......
---
title: Use warning icon in mini-graph if stage passed conditionally
merge_request: 8503
author:
...@@ -4,8 +4,11 @@ module Gitlab ...@@ -4,8 +4,11 @@ module Gitlab
module Build module Build
class Factory < Status::Factory class Factory < Status::Factory
def self.extended_statuses def self.extended_statuses
[Status::Build::Stop, Status::Build::Play, [[Status::Build::Cancelable,
Status::Build::Cancelable, Status::Build::Retryable] Status::Build::Retryable],
[Status::Build::FailedAllowed,
Status::Build::Play,
Status::Build::Stop]]
end end
def self.common_helpers def self.common_helpers
......
module Gitlab module Gitlab
module Ci module Ci
module Status module Status
module Pipeline module Build
class SuccessWithWarnings < SimpleDelegator class FailedAllowed < SimpleDelegator
include Status::Extended include Status::Extended
def text
'passed'
end
def label def label
'passed with warnings' 'failed (allowed to fail)'
end end
def icon def icon
...@@ -18,11 +14,11 @@ module Gitlab ...@@ -18,11 +14,11 @@ module Gitlab
end end
def group def group
'success_with_warnings' 'failed_with_warnings'
end end
def self.matches?(pipeline, user) def self.matches?(build, user)
pipeline.success? && pipeline.has_warnings? build.failed? && build.allow_failure?
end end
end end
end end
......
...@@ -5,41 +5,46 @@ module Gitlab ...@@ -5,41 +5,46 @@ module Gitlab
def initialize(subject, user) def initialize(subject, user)
@subject = subject @subject = subject
@user = user @user = user
@status = subject.status || HasStatus::DEFAULT_STATUS
end end
def fabricate! def fabricate!
if extended_status if extended_statuses.none?
extended_status.new(core_status)
else
core_status core_status
else
compound_extended_status
end end
end end
def self.extended_statuses def core_status
[] Gitlab::Ci::Status
.const_get(@status.capitalize)
.new(@subject, @user)
.extend(self.class.common_helpers)
end end
def self.common_helpers def compound_extended_status
Module.new extended_statuses.inject(core_status) do |status, extended|
extended.new(status)
end
end end
private def extended_statuses
return @extended_statuses if defined?(@extended_statuses)
def simple_status groups = self.class.extended_statuses.map do |group|
@simple_status ||= @subject.status || :created Array(group).find { |status| status.matches?(@subject, @user) }
end
@extended_statuses = groups.flatten.compact
end end
def core_status def self.extended_statuses
Gitlab::Ci::Status []
.const_get(simple_status.capitalize)
.new(@subject, @user)
.extend(self.class.common_helpers)
end end
def extended_status def self.common_helpers
@extended ||= self.class.extended_statuses.find do |status| Module.new
status.matches?(@subject, @user)
end
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Pipeline module Pipeline
class Factory < Status::Factory class Factory < Status::Factory
def self.extended_statuses def self.extended_statuses
[Pipeline::SuccessWithWarnings] [Status::SuccessWarning]
end end
def self.common_helpers def self.common_helpers
......
...@@ -3,6 +3,10 @@ module Gitlab ...@@ -3,6 +3,10 @@ module Gitlab
module Status module Status
module Stage module Stage
class Factory < Status::Factory class Factory < Status::Factory
def self.extended_statuses
[Status::SuccessWarning]
end
def self.common_helpers def self.common_helpers
Status::Stage::Common Status::Stage::Common
end end
......
module Gitlab
module Ci
module Status
##
# Extended status used when pipeline or stage passed conditionally.
# This means that failed jobs that are allowed to fail were present.
#
class SuccessWarning < SimpleDelegator
include Status::Extended
def text
'passed'
end
def label
'passed with warnings'
end
def icon
'icon_status_warning'
end
def group
'success_with_warnings'
end
def self.matches?(subject, user)
subject.success? && subject.has_warnings?
end
end
end
end
end
...@@ -3,11 +3,12 @@ FactoryGirl.define do ...@@ -3,11 +3,12 @@ FactoryGirl.define do
transient do transient do
name 'test' name 'test'
status nil status nil
warnings nil
pipeline factory: :ci_empty_pipeline pipeline factory: :ci_empty_pipeline
end end
initialize_with do initialize_with do
Ci::Stage.new(pipeline, name: name, status: status) Ci::Stage.new(pipeline, name: name, status: status, warnings: warnings)
end end
end end
end end
...@@ -3,15 +3,23 @@ require 'spec_helper' ...@@ -3,15 +3,23 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Build::Factory do describe Gitlab::Ci::Status::Build::Factory do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { build.project } let(:project) { build.project }
let(:status) { factory.fabricate! }
subject { described_class.new(build, user) } let(:factory) { described_class.new(build, user) }
let(:status) { subject.fabricate! }
before { project.team << [user, :developer] } before { project.team << [user, :developer] }
context 'when build is successful' do context 'when build is successful' do
let(:build) { create(:ci_build, :success) } let(:build) { create(:ci_build, :success) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable]
end
it 'fabricates a retryable build status' do it 'fabricates a retryable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
end end
...@@ -26,24 +34,72 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -26,24 +34,72 @@ describe Gitlab::Ci::Status::Build::Factory do
end end
context 'when build is failed' do context 'when build is failed' do
let(:build) { create(:ci_build, :failed) } context 'when build is not allowed to fail' do
let(:build) { create(:ci_build, :failed) }
it 'fabricates a retryable build status' do it 'matches correct core status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable expect(factory.core_status).to be_a Gitlab::Ci::Status::Failed
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable]
end
it 'fabricates a retryable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'failed'
expect(status.icon).to eq 'icon_status_failed'
expect(status.label).to eq 'failed'
expect(status).to have_details
expect(status).to have_action
end
end end
it 'fabricates status with correct details' do context 'when build is allowed to fail' do
expect(status.text).to eq 'failed' let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
expect(status.icon).to eq 'icon_status_failed'
expect(status.label).to eq 'failed' it 'matches correct core status' do
expect(status).to have_details expect(factory.core_status).to be_a Gitlab::Ci::Status::Failed
expect(status).to have_action end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable,
Gitlab::Ci::Status::Build::FailedAllowed]
end
it 'fabricates a failed but allowed build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::FailedAllowed
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'failed'
expect(status.icon).to eq 'icon_status_warning'
expect(status.label).to eq 'failed (allowed to fail)'
expect(status).to have_details
expect(status).to have_action
expect(status.action_title).to include 'Retry'
expect(status.action_path).to include 'retry'
end
end end
end end
context 'when build is a canceled' do context 'when build is a canceled' do
let(:build) { create(:ci_build, :canceled) } let(:build) { create(:ci_build, :canceled) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Canceled
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable]
end
it 'fabricates a retryable build status' do it 'fabricates a retryable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
end end
...@@ -60,6 +116,15 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -60,6 +116,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is running' do context 'when build is running' do
let(:build) { create(:ci_build, :running) } let(:build) { create(:ci_build, :running) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Running
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Cancelable]
end
it 'fabricates a canceable build status' do it 'fabricates a canceable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end end
...@@ -76,6 +141,15 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -76,6 +141,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is pending' do context 'when build is pending' do
let(:build) { create(:ci_build, :pending) } let(:build) { create(:ci_build, :pending) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Pending
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Cancelable]
end
it 'fabricates a cancelable build status' do it 'fabricates a cancelable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end end
...@@ -92,6 +166,14 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -92,6 +166,14 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is skipped' do context 'when build is skipped' do
let(:build) { create(:ci_build, :skipped) } let(:build) { create(:ci_build, :skipped) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Skipped
end
it 'does not match extended statuses' do
expect(factory.extended_statuses).to be_empty
end
it 'fabricates a core skipped status' do it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Skipped expect(status).to be_a Gitlab::Ci::Status::Skipped
end end
...@@ -109,6 +191,15 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -109,6 +191,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is a play action' do context 'when build is a play action' do
let(:build) { create(:ci_build, :playable) } let(:build) { create(:ci_build, :playable) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Skipped
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Play]
end
it 'fabricates a core skipped status' do it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Play expect(status).to be_a Gitlab::Ci::Status::Build::Play
end end
...@@ -119,12 +210,22 @@ describe Gitlab::Ci::Status::Build::Factory do ...@@ -119,12 +210,22 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.label).to eq 'manual play action' expect(status.label).to eq 'manual play action'
expect(status).to have_details expect(status).to have_details
expect(status).to have_action expect(status).to have_action
expect(status.action_path).to include 'play'
end end
end end
context 'when build is an environment stop action' do context 'when build is an environment stop action' do
let(:build) { create(:ci_build, :playable, :teardown_environment) } let(:build) { create(:ci_build, :playable, :teardown_environment) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Skipped
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Stop]
end
it 'fabricates a core skipped status' do it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Stop expect(status).to be_a Gitlab::Ci::Status::Build::Stop
end end
......
require 'spec_helper'
describe Gitlab::Ci::Status::Build::FailedAllowed do
let(:status) { double('core status') }
let(:user) { double('user') }
subject do
described_class.new(status)
end
describe '#text' do
it 'does not override status text' do
expect(status).to receive(:text)
subject.text
end
end
describe '#icon' do
it 'returns a warning icon' do
expect(subject.icon).to eq 'icon_status_warning'
end
end
describe '#label' do
it 'returns information about failed but allowed to fail status' do
expect(subject.label).to eq 'failed (allowed to fail)'
end
end
describe '#group' do
it 'returns status failed with warnings status group' do
expect(subject.group).to eq 'failed_with_warnings'
end
end
describe 'action details' do
describe '#has_action?' do
it 'does not decorate action details' do
expect(status).to receive(:has_action?)
subject.has_action?
end
end
describe '#action_path' do
it 'does not decorate action path' do
expect(status).to receive(:action_path)
subject.action_path
end
end
describe '#action_icon' do
it 'does not decorate action icon' do
expect(status).to receive(:action_icon)
subject.action_icon
end
end
describe '#action_title' do
it 'does not decorate action title' do
expect(status).to receive(:action_title)
subject.action_title
end
end
end
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is failed' do
context 'when build is allowed to fail' do
let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
it 'is a correct match' do
expect(subject).to be true
end
end
context 'when build is not allowed to fail' do
let(:build) { create(:ci_build, :failed) }
it 'is not a correct match' do
expect(subject).not_to be true
end
end
end
context 'when build did not fail' do
context 'when build is allowed to fail' do
let(:build) { create(:ci_build, :success, :allowed_to_fail) }
it 'is not a correct match' do
expect(subject).not_to be true
end
end
context 'when build is not allowed to fail' do
let(:build) { create(:ci_build, :success) }
it 'is not a correct match' do
expect(subject).not_to be true
end
end
end
end
end
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Status::Factory do describe Gitlab::Ci::Status::Factory do
subject do let(:user) { create(:user) }
described_class.new(resource, user) let(:fabricated_status) { factory.fabricate! }
let(:factory) { described_class.new(resource, user) }
context 'when object has a core status' do
HasStatus::AVAILABLE_STATUSES.each do |simple_status|
context "when simple core status is #{simple_status}" do
let(:resource) { double('resource', status: simple_status) }
let(:expected_status) do
Gitlab::Ci::Status.const_get(simple_status.capitalize)
end
it "fabricates a core status #{simple_status}" do
expect(fabricated_status).to be_a expected_status
end
it "matches a valid core status for #{simple_status}" do
expect(factory.core_status).to be_a expected_status
end
it "does not match any extended statuses for #{simple_status}" do
expect(factory.extended_statuses).to be_empty
end
end
end
end end
let(:user) { create(:user) } context 'when resource supports multiple extended statuses' do
let(:resource) { double('resource', status: :success) }
let(:status) { subject.fabricate! } let(:first_extended_status) do
Class.new(SimpleDelegator) do
def first_method
'first return value'
end
context 'when object has a core status' do def second_method
HasStatus::AVAILABLE_STATUSES.each do |core_status| 'second return value'
context "when core status is #{core_status}" do end
let(:resource) { double(status: core_status) }
def self.matches?(*)
true
end
end
end
it "fabricates a core status #{core_status}" do let(:second_extended_status) do
expect(status).to be_a( Class.new(SimpleDelegator) do
Gitlab::Ci::Status.const_get(core_status.capitalize)) def first_method
'decorated return value'
end end
def third_method
'third return value'
end
def self.matches?(*)
true
end
end
end
shared_examples 'compound decorator factory' do
it 'fabricates compound decorator' do
expect(fabricated_status.first_method).to eq 'decorated return value'
expect(fabricated_status.second_method).to eq 'second return value'
expect(fabricated_status.third_method).to eq 'third return value'
end end
it 'delegates to core status' do
expect(fabricated_status.text).to eq 'passed'
end
it 'latest matches status becomes a status name' do
expect(fabricated_status.class).to eq second_extended_status
end
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [first_extended_status, second_extended_status]
end
end
context 'when exclusive statuses are matches' do
before do
allow(described_class).to receive(:extended_statuses)
.and_return([[first_extended_status, second_extended_status]])
end
it 'does not fabricate compound decorator' do
expect(fabricated_status.first_method).to eq 'first return value'
expect(fabricated_status.second_method).to eq 'second return value'
expect(fabricated_status).not_to respond_to(:third_method)
end
it 'delegates to core status' do
expect(fabricated_status.text).to eq 'passed'
end
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses).to eq [first_extended_status]
end
end
context 'when exclusive statuses are not matched' do
before do
allow(described_class).to receive(:extended_statuses)
.and_return([[first_extended_status], [second_extended_status]])
end
it_behaves_like 'compound decorator factory'
end
context 'when using simplified status grouping' do
before do
allow(described_class).to receive(:extended_statuses)
.and_return([first_extended_status, second_extended_status])
end
it_behaves_like 'compound decorator factory'
end end
end end
end end
...@@ -3,29 +3,32 @@ require 'spec_helper' ...@@ -3,29 +3,32 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::Factory do describe Gitlab::Ci::Status::Pipeline::Factory do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { pipeline.project } let(:project) { pipeline.project }
let(:status) { factory.fabricate! }
subject do let(:factory) { described_class.new(pipeline, user) }
described_class.new(pipeline, user)
end
let(:status) do
subject.fabricate!
end
before do before do
project.team << [user, :developer] project.team << [user, :developer]
end end
context 'when pipeline has a core status' do context 'when pipeline has a core status' do
HasStatus::AVAILABLE_STATUSES.each do |core_status| HasStatus::AVAILABLE_STATUSES.each do |simple_status|
context "when core status is #{core_status}" do context "when core status is #{simple_status}" do
let(:pipeline) do let(:pipeline) { create(:ci_pipeline, status: simple_status) }
create(:ci_pipeline, status: core_status)
let(:expected_status) do
Gitlab::Ci::Status.const_get(simple_status.capitalize)
end
it "matches correct core status for #{simple_status}" do
expect(factory.core_status).to be_a expected_status
end end
it "fabricates a core status #{core_status}" do it 'does not matche extended statuses' do
expect(status).to be_a( expect(factory.extended_statuses).to be_empty
Gitlab::Ci::Status.const_get(core_status.capitalize)) end
it "fabricates a core status #{simple_status}" do
expect(status).to be_a expected_status
end end
it 'extends core status with common pipeline methods' do it 'extends core status with common pipeline methods' do
...@@ -47,13 +50,22 @@ describe Gitlab::Ci::Status::Pipeline::Factory do ...@@ -47,13 +50,22 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline) create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
end end
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::SuccessWarning]
end
it 'fabricates extended "success with warnings" status' do it 'fabricates extended "success with warnings" status' do
expect(status) expect(status).to be_a Gitlab::Ci::Status::SuccessWarning
.to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings
end end
it 'extends core status with common pipeline methods' do it 'extends core status with common pipeline method' do
expect(status).to have_details expect(status).to have_details
expect(status.details_path).to include "pipelines/#{pipeline.id}"
end end
end end
end end
...@@ -43,4 +43,25 @@ describe Gitlab::Ci::Status::Stage::Factory do ...@@ -43,4 +43,25 @@ describe Gitlab::Ci::Status::Stage::Factory do
end end
end end
end end
context 'when stage has warnings' do
let(:stage) do
build(:ci_stage, name: 'test', status: :success, pipeline: pipeline)
end
before do
create(:ci_build, :allowed_to_fail, :failed,
stage: 'test', pipeline: stage.pipeline)
end
it 'fabricates extended "success with warnings" status' do
expect(status)
.to be_a Gitlab::Ci::Status::SuccessWarning
end
it 'extends core status with common stage method' do
expect(status).to have_details
expect(status.details_path).to include "pipelines/#{pipeline.id}##{stage.name}"
end
end
end end
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do describe Gitlab::Ci::Status::SuccessWarning do
subject do subject do
described_class.new(double('status')) described_class.new(double('status'))
end end
...@@ -22,46 +22,52 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do ...@@ -22,46 +22,52 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
end end
describe '.matches?' do describe '.matches?' do
context 'when pipeline is successful' do let(:matchable) { double('matchable') }
let(:pipeline) do
create(:ci_pipeline, status: :success) context 'when matchable subject is successful' do
before do
allow(matchable).to receive(:success?).and_return(true)
end end
context 'when pipeline has warnings' do context 'when matchable subject has warnings' do
before do before do
allow(pipeline).to receive(:has_warnings?).and_return(true) allow(matchable).to receive(:has_warnings?).and_return(true)
end end
it 'is a correct match' do it 'is a correct match' do
expect(described_class.matches?(pipeline, double)).to eq true expect(described_class.matches?(matchable, double)).to eq true
end end
end end
context 'when pipeline does not have warnings' do context 'when matchable subject does not have warnings' do
before do
allow(matchable).to receive(:has_warnings?).and_return(false)
end
it 'does not match' do it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false expect(described_class.matches?(matchable, double)).to eq false
end end
end end
end end
context 'when pipeline is not successful' do context 'when matchable subject is not successful' do
let(:pipeline) do before do
create(:ci_pipeline, status: :skipped) allow(matchable).to receive(:success?).and_return(false)
end end
context 'when pipeline has warnings' do context 'when matchable subject has warnings' do
before do before do
allow(pipeline).to receive(:has_warnings?).and_return(true) allow(matchable).to receive(:has_warnings?).and_return(true)
end end
it 'does not match' do it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false expect(described_class.matches?(matchable, double)).to eq false
end end
end end
context 'when pipeline does not have warnings' do context 'when matchable subject does not have warnings' do
it 'does not match' do it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false expect(described_class.matches?(matchable, double)).to eq false
end end
end end
end end
......
...@@ -122,55 +122,80 @@ describe Ci::Pipeline, models: true do ...@@ -122,55 +122,80 @@ describe Ci::Pipeline, models: true do
end end
end end
describe '#stages' do describe 'pipeline stages' do
before do before do
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success') create(:commit_status, pipeline: pipeline,
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed') stage: 'build',
create(:commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running') name: 'linux',
create(:commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success') stage_idx: 0,
end status: 'success')
subject { pipeline.stages } create(:commit_status, pipeline: pipeline,
stage: 'build',
context 'stages list' do name: 'mac',
it 'returns ordered list of stages' do stage_idx: 0,
expect(subject.map(&:name)).to eq(%w[build test deploy]) status: 'failed')
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'staging',
stage_idx: 2,
status: 'running')
create(:commit_status, pipeline: pipeline,
stage: 'test',
name: 'rspec',
stage_idx: 1,
status: 'success')
end
describe '#stages' do
subject { pipeline.stages }
context 'stages list' do
it 'returns ordered list of stages' do
expect(subject.map(&:name)).to eq(%w[build test deploy])
end
end end
end
it 'returns a valid number of stages' do context 'stages with statuses' do
expect(pipeline.stages_count).to eq(3) let(:statuses) do
end subject.map { |stage| [stage.name, stage.status] }
end
it 'returns a valid names of stages' do it 'returns list of stages with correct statuses' do
expect(pipeline.stages_name).to eq(['build', 'test', 'deploy']) expect(statuses).to eq([['build', 'failed'],
end ['test', 'success'],
['deploy', 'running']])
end
context 'stages with statuses' do context 'when commit status is retried' do
let(:statuses) do before do
subject.map do |stage| create(:commit_status, pipeline: pipeline,
[stage.name, stage.status] stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'success')
end
it 'ignores the previous state' do
expect(statuses).to eq([['build', 'success'],
['test', 'success'],
['deploy', 'running']])
end
end end
end end
end
it 'returns list of stages with statuses' do describe '#stages_count' do
expect(statuses).to eq([['build', 'failed'], it 'returns a valid number of stages' do
['test', 'success'], expect(pipeline.stages_count).to eq(3)
['deploy', 'running']
])
end end
end
context 'when build is retried' do describe '#stages_name' do
before do it 'returns a valid names of stages' do
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success') expect(pipeline.stages_name).to eq(['build', 'test', 'deploy'])
end
it 'ignores the previous state' do
expect(statuses).to eq([['build', 'success'],
['test', 'success'],
['deploy', 'running']
])
end
end end
end end
end end
......
...@@ -142,6 +142,78 @@ describe Ci::Stage, models: true do ...@@ -142,6 +142,78 @@ describe Ci::Stage, models: true do
end end
end end
describe '#success?' do
context 'when stage is successful' do
before do
create_job(:ci_build, status: :success)
create_job(:generic_commit_status, status: :success)
end
it 'is successful' do
expect(stage).to be_success
end
end
context 'when stage is not successful' do
before do
create_job(:ci_build, status: :failed)
create_job(:generic_commit_status, status: :success)
end
it 'is not successful' do
expect(stage).not_to be_success
end
end
end
describe '#has_warnings?' do
context 'when stage has warnings' do
context 'when using memoized warnings flag' do
context 'when there are warnings' do
let(:stage) { build(:ci_stage, warnings: true) }
it 'has memoized warnings' do
expect(stage).not_to receive(:statuses)
expect(stage).to have_warnings
end
end
context 'when there are no warnings' do
let(:stage) { build(:ci_stage, warnings: false) }
it 'has memoized warnings' do
expect(stage).not_to receive(:statuses)
expect(stage).not_to have_warnings
end
end
end
context 'when calculating warnings from statuses' do
before do
create(:ci_build, :failed, :allowed_to_fail,
stage: stage_name, pipeline: pipeline)
end
it 'has warnings calculated from statuses' do
expect(stage).to receive(:statuses).and_call_original
expect(stage).to have_warnings
end
end
end
context 'when stage does not have warnings' do
before do
create(:ci_build, :success, stage: stage_name,
pipeline: pipeline)
end
it 'does not have warnings calculated from statuses' do
expect(stage).to receive(:statuses).and_call_original
expect(stage).not_to have_warnings
end
end
end
def create_job(type, status: 'success', stage: stage_name) def create_job(type, status: 'success', stage: stage_name)
create(type, pipeline: pipeline, stage: stage, status: status) create(type, pipeline: pipeline, stage: stage, status: status)
end end
......
...@@ -219,4 +219,10 @@ describe HasStatus do ...@@ -219,4 +219,10 @@ describe HasStatus do
end end
end end
end end
describe '::DEFAULT_STATUS' do
it 'is a status created' do
expect(described_class::DEFAULT_STATUS).to eq 'created'
end
end
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