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
This commit is contained in:
Kamil Trzciński 2017-01-23 14:32:06 +00:00
commit 7446c2f5cf
24 changed files with 745 additions and 214 deletions

View file

@ -15,6 +15,7 @@
}
.ci-status-icon-pending,
.ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings {
color: $gl-warning;

View file

@ -19,7 +19,8 @@
overflow: visible;
}
&.ci-failed {
&.ci-failed,
&.ci-failed_with_warnings {
color: $gl-danger;
border-color: $gl-danger;

View file

@ -128,16 +128,21 @@ module Ci
end
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
stages_query = statuses.group('stage').select(:stage)
.order('max(stage_idx)')
warnings_sql = statuses.latest.select('COUNT(*) > 0')
.where('stage=sg.stage').failed_but_allowed.to_sql
stages_with_statuses = CommitStatus.from(stages_query, :sg).
pluck('sg.stage', status_sql)
stages_with_statuses = CommitStatus.from(stages_query, :sg)
.pluck('sg.stage', status_sql, "(#{warnings_sql})")
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

View file

@ -8,10 +8,11 @@ module Ci
delegate :project, to: :pipeline
def initialize(pipeline, name:, status: nil)
def initialize(pipeline, name:, status: nil, warnings: nil)
@pipeline = pipeline
@name = name
@status = status
@warnings = warnings
end
def to_param
@ -39,5 +40,17 @@ module Ci
def builds
@builds ||= pipeline.builds.where(stage: name)
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

View file

@ -1,6 +1,7 @@
module HasStatus
extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]
STARTED_STATUSES = %w[running success failed skipped]
ACTIVE_STATUSES = %w[pending running]

View file

@ -0,0 +1,4 @@
---
title: Use warning icon in mini-graph if stage passed conditionally
merge_request: 8503
author:

View file

@ -4,8 +4,11 @@ module Gitlab
module Build
class Factory < Status::Factory
def self.extended_statuses
[Status::Build::Stop, Status::Build::Play,
Status::Build::Cancelable, Status::Build::Retryable]
[[Status::Build::Cancelable,
Status::Build::Retryable],
[Status::Build::FailedAllowed,
Status::Build::Play,
Status::Build::Stop]]
end
def self.common_helpers

View file

@ -0,0 +1,27 @@
module Gitlab
module Ci
module Status
module Build
class FailedAllowed < SimpleDelegator
include Status::Extended
def label
'failed (allowed to fail)'
end
def icon
'icon_status_warning'
end
def group
'failed_with_warnings'
end
def self.matches?(build, user)
build.failed? && build.allow_failure?
end
end
end
end
end
end

View file

@ -5,16 +5,40 @@ module Gitlab
def initialize(subject, user)
@subject = subject
@user = user
@status = subject.status || HasStatus::DEFAULT_STATUS
end
def fabricate!
if extended_status
extended_status.new(core_status)
else
if extended_statuses.none?
core_status
else
compound_extended_status
end
end
def core_status
Gitlab::Ci::Status
.const_get(@status.capitalize)
.new(@subject, @user)
.extend(self.class.common_helpers)
end
def compound_extended_status
extended_statuses.inject(core_status) do |status, extended|
extended.new(status)
end
end
def extended_statuses
return @extended_statuses if defined?(@extended_statuses)
groups = self.class.extended_statuses.map do |group|
Array(group).find { |status| status.matches?(@subject, @user) }
end
@extended_statuses = groups.flatten.compact
end
def self.extended_statuses
[]
end
@ -22,25 +46,6 @@ module Gitlab
def self.common_helpers
Module.new
end
private
def simple_status
@simple_status ||= @subject.status || :created
end
def core_status
Gitlab::Ci::Status
.const_get(simple_status.capitalize)
.new(@subject, @user)
.extend(self.class.common_helpers)
end
def extended_status
@extended ||= self.class.extended_statuses.find do |status|
status.matches?(@subject, @user)
end
end
end
end
end

View file

@ -4,7 +4,7 @@ module Gitlab
module Pipeline
class Factory < Status::Factory
def self.extended_statuses
[Pipeline::SuccessWithWarnings]
[Status::SuccessWarning]
end
def self.common_helpers

View file

@ -1,31 +0,0 @@
module Gitlab
module Ci
module Status
module Pipeline
class SuccessWithWarnings < 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?(pipeline, user)
pipeline.success? && pipeline.has_warnings?
end
end
end
end
end
end

View file

@ -3,6 +3,10 @@ module Gitlab
module Status
module Stage
class Factory < Status::Factory
def self.extended_statuses
[Status::SuccessWarning]
end
def self.common_helpers
Status::Stage::Common
end

View file

@ -0,0 +1,33 @@
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

View file

@ -3,11 +3,12 @@ FactoryGirl.define do
transient do
name 'test'
status nil
warnings nil
pipeline factory: :ci_empty_pipeline
end
initialize_with do
Ci::Stage.new(pipeline, name: name, status: status)
Ci::Stage.new(pipeline, name: name, status: status, warnings: warnings)
end
end
end

View file

@ -3,15 +3,23 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Build::Factory do
let(:user) { create(:user) }
let(:project) { build.project }
subject { described_class.new(build, user) }
let(:status) { subject.fabricate! }
let(:status) { factory.fabricate! }
let(:factory) { described_class.new(build, user) }
before { project.team << [user, :developer] }
context 'when build is successful' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
end
@ -26,24 +34,72 @@ describe Gitlab::Ci::Status::Build::Factory do
end
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
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
it 'matches correct core status' do
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
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
context 'when build is allowed to fail' do
let(:build) { create(:ci_build, :failed, :allowed_to_fail) }
it 'matches correct core status' do
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,
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
context 'when build is a canceled' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
end
@ -60,6 +116,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is running' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end
@ -76,6 +141,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is pending' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end
@ -92,6 +166,14 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is skipped' do
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
expect(status).to be_a Gitlab::Ci::Status::Skipped
end
@ -109,6 +191,15 @@ describe Gitlab::Ci::Status::Build::Factory do
context 'when build is a play action' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Play
end
@ -119,12 +210,22 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.label).to eq 'manual play action'
expect(status).to have_details
expect(status).to have_action
expect(status.action_path).to include 'play'
end
end
context 'when build is an environment stop action' do
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
expect(status).to be_a Gitlab::Ci::Status::Build::Stop
end

View file

@ -0,0 +1,110 @@
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

View file

@ -1,24 +1,135 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Factory do
subject do
described_class.new(resource, user)
end
let(:user) { create(:user) }
let(:status) { subject.fabricate! }
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 |core_status|
context "when core status is #{core_status}" do
let(:resource) { double(status: core_status) }
HasStatus::AVAILABLE_STATUSES.each do |simple_status|
context "when simple core status is #{simple_status}" do
let(:resource) { double('resource', status: simple_status) }
it "fabricates a core status #{core_status}" do
expect(status).to be_a(
Gitlab::Ci::Status.const_get(core_status.capitalize))
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
context 'when resource supports multiple extended statuses' do
let(:resource) { double('resource', status: :success) }
let(:first_extended_status) do
Class.new(SimpleDelegator) do
def first_method
'first return value'
end
def second_method
'second return value'
end
def self.matches?(*)
true
end
end
end
let(:second_extended_status) do
Class.new(SimpleDelegator) do
def first_method
'decorated return value'
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
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

View file

@ -3,29 +3,32 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::Factory do
let(:user) { create(:user) }
let(:project) { pipeline.project }
subject do
described_class.new(pipeline, user)
end
let(:status) do
subject.fabricate!
end
let(:status) { factory.fabricate! }
let(:factory) { described_class.new(pipeline, user) }
before do
project.team << [user, :developer]
end
context 'when pipeline has a core status' do
HasStatus::AVAILABLE_STATUSES.each do |core_status|
context "when core status is #{core_status}" do
let(:pipeline) do
create(:ci_pipeline, status: core_status)
HasStatus::AVAILABLE_STATUSES.each do |simple_status|
context "when core status is #{simple_status}" do
let(:pipeline) { create(:ci_pipeline, status: simple_status) }
let(:expected_status) do
Gitlab::Ci::Status.const_get(simple_status.capitalize)
end
it "fabricates a core status #{core_status}" do
expect(status).to be_a(
Gitlab::Ci::Status.const_get(core_status.capitalize))
it "matches correct core status for #{simple_status}" do
expect(factory.core_status).to be_a expected_status
end
it 'does not matche extended statuses' do
expect(factory.extended_statuses).to be_empty
end
it "fabricates a core status #{simple_status}" do
expect(status).to be_a expected_status
end
it 'extends core status with common pipeline methods' do
@ -47,13 +50,22 @@ describe Gitlab::Ci::Status::Pipeline::Factory do
create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
end
it 'fabricates extended "success with warnings" status' do
expect(status)
.to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
end
it 'extends core status with common pipeline methods' do
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
expect(status).to be_a Gitlab::Ci::Status::SuccessWarning
end
it 'extends core status with common pipeline method' do
expect(status).to have_details
expect(status.details_path).to include "pipelines/#{pipeline.id}"
end
end
end

View file

@ -1,69 +0,0 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
subject do
described_class.new(double('status'))
end
describe '#test' do
it { expect(subject.text).to eq 'passed' }
end
describe '#label' do
it { expect(subject.label).to eq 'passed with warnings' }
end
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_warning' }
end
describe '#group' do
it { expect(subject.group).to eq 'success_with_warnings' }
end
describe '.matches?' do
context 'when pipeline is successful' do
let(:pipeline) do
create(:ci_pipeline, status: :success)
end
context 'when pipeline has warnings' do
before do
allow(pipeline).to receive(:has_warnings?).and_return(true)
end
it 'is a correct match' do
expect(described_class.matches?(pipeline, double)).to eq true
end
end
context 'when pipeline does not have warnings' do
it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false
end
end
end
context 'when pipeline is not successful' do
let(:pipeline) do
create(:ci_pipeline, status: :skipped)
end
context 'when pipeline has warnings' do
before do
allow(pipeline).to receive(:has_warnings?).and_return(true)
end
it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false
end
end
context 'when pipeline does not have warnings' do
it 'does not match' do
expect(described_class.matches?(pipeline, double)).to eq false
end
end
end
end
end

View file

@ -43,4 +43,25 @@ describe Gitlab::Ci::Status::Stage::Factory do
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

View file

@ -0,0 +1,75 @@
require 'spec_helper'
describe Gitlab::Ci::Status::SuccessWarning do
subject do
described_class.new(double('status'))
end
describe '#test' do
it { expect(subject.text).to eq 'passed' }
end
describe '#label' do
it { expect(subject.label).to eq 'passed with warnings' }
end
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_warning' }
end
describe '#group' do
it { expect(subject.group).to eq 'success_with_warnings' }
end
describe '.matches?' do
let(:matchable) { double('matchable') }
context 'when matchable subject is successful' do
before do
allow(matchable).to receive(:success?).and_return(true)
end
context 'when matchable subject has warnings' do
before do
allow(matchable).to receive(:has_warnings?).and_return(true)
end
it 'is a correct match' do
expect(described_class.matches?(matchable, double)).to eq true
end
end
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
expect(described_class.matches?(matchable, double)).to eq false
end
end
end
context 'when matchable subject is not successful' do
before do
allow(matchable).to receive(:success?).and_return(false)
end
context 'when matchable subject has warnings' do
before do
allow(matchable).to receive(:has_warnings?).and_return(true)
end
it 'does not match' do
expect(described_class.matches?(matchable, double)).to eq false
end
end
context 'when matchable subject does not have warnings' do
it 'does not match' do
expect(described_class.matches?(matchable, double)).to eq false
end
end
end
end
end

View file

@ -122,55 +122,80 @@ describe Ci::Pipeline, models: true do
end
end
describe '#stages' do
describe 'pipeline stages' do
before do
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success')
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, 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')
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'linux',
stage_idx: 0,
status: 'success')
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'mac',
stage_idx: 0,
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
subject { pipeline.stages }
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
it 'returns a valid number of stages' do
expect(pipeline.stages_count).to eq(3)
end
it 'returns a valid names of stages' do
expect(pipeline.stages_name).to eq(['build', 'test', 'deploy'])
end
context 'stages with statuses' do
let(:statuses) do
subject.map do |stage|
[stage.name, stage.status]
context 'stages list' do
it 'returns ordered list of stages' do
expect(subject.map(&:name)).to eq(%w[build test deploy])
end
end
it 'returns list of stages with statuses' do
expect(statuses).to eq([['build', 'failed'],
['test', 'success'],
['deploy', 'running']
])
end
context 'when build is retried' do
before do
create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success')
context 'stages with statuses' do
let(:statuses) do
subject.map { |stage| [stage.name, stage.status] }
end
it 'ignores the previous state' do
expect(statuses).to eq([['build', 'success'],
it 'returns list of stages with correct statuses' do
expect(statuses).to eq([['build', 'failed'],
['test', 'success'],
['deploy', 'running']
])
['deploy', 'running']])
end
context 'when commit status is retried' do
before do
create(:commit_status, pipeline: pipeline,
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
describe '#stages_count' do
it 'returns a valid number of stages' do
expect(pipeline.stages_count).to eq(3)
end
end
describe '#stages_name' do
it 'returns a valid names of stages' do
expect(pipeline.stages_name).to eq(['build', 'test', 'deploy'])
end
end
end

View file

@ -142,6 +142,78 @@ describe Ci::Stage, models: true do
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)
create(type, pipeline: pipeline, stage: stage, status: status)
end

View file

@ -219,4 +219,10 @@ describe HasStatus do
end
end
end
describe '::DEFAULT_STATUS' do
it 'is a status created' do
expect(described_class::DEFAULT_STATUS).to eq 'created'
end
end
end