Merge remote-tracking branch 'upstream/master' into show-commit-status-from-latest-pipeline

* upstream/master: (39 commits)
  Improve build status specs contexts descriptions
  Add some missing tests for detailed status methods
  Remove trailing blank line from Allowable module
  Update manual build icon SVG
  Make it possible to mix `Gitlab::Routing` in
  Extract abilities checking module from ability model
  Extend tests for pipeline detailed status helpers
  Add tests for common build detailed status helpers
  Add missing tests for build `cancelable?` method
  Add tests for detailed build statuses factory
  Make it possible to retry build that was canceled
  Make build retryable only if complete and executed
  Improve readability in methods for detailed status
  Add tests for build cancelable/retryable statuses
  Extend specs for build play/stop detailed statuses
  Refine build stop/play extended status specs
  Use manual build icon in play/stop build statuses
  Adds manual action icon and case to show it
  Fix detailed status specs for pipeline stage model
  Fix detailed status badge for generic commit status
  ...
This commit is contained in:
Lin Jen-Shin 2016-12-14 20:42:42 +08:00
commit 3ce6ba7db9
58 changed files with 1126 additions and 171 deletions

View file

@ -4,25 +4,7 @@ module CiStatusHelper
builds_namespace_project_commit_path(project.namespace, project, pipeline.sha)
end
def ci_status_with_icon(status, target = nil)
content = ci_icon_for_status(status) + ci_text_for_status(status)
klass = "ci-status ci-#{status}"
if target
link_to content, target, class: klass
else
content_tag :span, content, class: klass
end
end
def ci_text_for_status(status)
if detailed_status?(status)
status.text
else
status
end
end
# Is used by Commit and Merge Request Widget
def ci_label_for_status(status)
if detailed_status?(status)
return status.label

View file

@ -100,6 +100,12 @@ module Ci
end
end
def detailed_status(current_user)
Gitlab::Ci::Status::Build::Factory
.new(self, current_user)
.fabricate!
end
def manual?
self.when == 'manual'
end
@ -123,8 +129,13 @@ module Ci
end
end
def cancelable?
active?
end
def retryable?
project.builds_enabled? && commands.present? && complete?
project.builds_enabled? && commands.present? &&
(success? || failed? || canceled?)
end
def retried?
@ -148,7 +159,7 @@ module Ci
end
def environment_action
self.options.fetch(:environment, {}).fetch(:action, 'start')
self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options
end
def outdated_deployment?

View file

@ -346,8 +346,10 @@ module Ci
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
def detailed_status
Gitlab::Ci::Status::Pipeline::Factory.new(self).fabricate!
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user)
.fabricate!
end
private

View file

@ -22,8 +22,10 @@ module Ci
@status ||= statuses.latest.status
end
def detailed_status
Gitlab::Ci::Status::Stage::Factory.new(self).fabricate!
def detailed_status(current_user)
Gitlab::Ci::Status::Stage::Factory
.new(self, current_user)
.fabricate!
end
def statuses

View file

@ -131,4 +131,10 @@ class CommitStatus < ActiveRecord::Base
def has_trace?
false
end
def detailed_status(current_user)
Gitlab::Ci::Status::Factory
.new(self, current_user)
.fabricate!
end
end

View file

@ -91,7 +91,7 @@
%strong ##{build.id}
%td.status
= ci_status_with_icon(build.status)
= render 'ci/status/badge', status: build.detailed_status(current_user)
%td.status
- if project

View file

@ -0,0 +1,10 @@
- status = local_assigns.fetch(:status)
- if status.has_details?
= link_to status.details_path, class: "ci-status ci-#{status}" do
= custom_icon(status.icon)
= status.text
- else
%span{ class: "ci-status ci-#{status}" }
= custom_icon(status.icon)
= status.text

View file

@ -1,6 +1,6 @@
.content-block.build-header
.header-content
= ci_status_with_icon(@build.status)
= render 'ci/status/badge', status: @build.detailed_status(current_user)
Build
%strong ##{@build.id}
in pipeline

View file

@ -9,10 +9,7 @@
%tr.build.commit{class: ('retried' if retried)}
%td.status
- if can?(current_user, :read_build, build)
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
- else
= ci_status_with_icon(build.status)
= render "ci/status/badge", status: build.detailed_status(current_user)
%td.branch-commit
- if can?(current_user, :read_build, build)

View file

@ -1,13 +1,10 @@
- status = pipeline.status
- detailed_status = pipeline.detailed_status
- show_commit = local_assigns.fetch(:show_commit, true)
- show_branch = local_assigns.fetch(:show_branch, true)
%tr.commit
%td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{detailed_status}" do
= ci_icon_for_status(detailed_status)
= ci_text_for_status(detailed_status)
= render 'ci/status/badge', status: pipeline.detailed_status(current_user)
%td
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do

View file

@ -8,10 +8,7 @@
%tr.generic_commit_status{class: ('retried' if retried)}
%td.status
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url
= ci_status_with_icon(generic_commit_status.status, generic_commit_status.target_url)
- else
= ci_status_with_icon(generic_commit_status.status)
= render 'ci/status/badge', status: generic_commit_status.detailed_status(current_user)
%td.generic_commit_status-link
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url

View file

@ -1,6 +1,6 @@
.page-content-header
.header-main-content
= ci_status_with_icon(@pipeline.detailed_status)
= render 'ci/status/badge', status: @pipeline.detailed_status(current_user)
%strong Pipeline ##{@commit.pipelines.last.id}
triggered #{time_ago_with_tooltip(@commit.authored_date)} by
= author_avatar(@commit, size: 24)

View file

@ -19,4 +19,4 @@
%li.build
.curve
.dropdown.inline.build-content
= render "projects/stage/in_stage_group", name: group_name, subject: grouped_statuses
= render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses

View file

@ -0,0 +1 @@
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M10.5 7.63V6.37l-.787-.13c-.044-.175-.132-.349-.263-.61l.481-.652-.918-.913-.657.478a2.346 2.346 0 0 0-.612-.26L7.656 3.5H6.388l-.132.783c-.219.043-.394.13-.612.26l-.657-.478-.918.913.437.652c-.131.218-.175.392-.262.61l-.744.086v1.261l.787.13c.044.218.132.392.263.61l-.438.651.92.913.655-.434c.175.086.394.173.613.26l.131.783h1.313l.131-.783c.219-.043.394-.13.613-.26l.656.478.918-.913-.48-.652c.13-.218.218-.435.262-.61l.656-.13zM7 8.283a1.285 1.285 0 0 1-1.313-1.305c0-.739.57-1.304 1.313-1.304.744 0 1.313.565 1.313 1.304 0 .74-.57 1.305-1.313 1.305z"/></g></svg>

After

Width:  |  Height:  |  Size: 787 B

7
lib/gitlab/allowable.rb Normal file
View file

@ -0,0 +1,7 @@
module Gitlab
module Allowable
def can?(user, action, subject)
Ability.allowed?(user, action, subject)
end
end
end

View file

@ -0,0 +1,37 @@
module Gitlab
module Ci
module Status
module Build
class Cancelable < SimpleDelegator
include Status::Extended
def has_action?
can?(user, :update_build, subject)
end
def action_icon
'ban'
end
def action_path
cancel_namespace_project_build_path(subject.project.namespace,
subject.project,
subject)
end
def action_method
:post
end
def action_title
'Cancel'
end
def self.matches?(build, user)
build.cancelable?
end
end
end
end
end
end

View file

@ -0,0 +1,19 @@
module Gitlab
module Ci
module Status
module Build
module Common
def has_details?
can?(user, :read_build, subject)
end
def details_path
namespace_project_build_path(subject.project.namespace,
subject.project,
subject)
end
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Gitlab
module Ci
module Status
module Build
class Factory < Status::Factory
def self.extended_statuses
[Status::Build::Stop, Status::Build::Play,
Status::Build::Cancelable, Status::Build::Retryable]
end
def self.common_helpers
Status::Build::Common
end
end
end
end
end
end

View file

@ -0,0 +1,53 @@
module Gitlab
module Ci
module Status
module Build
class Play < SimpleDelegator
include Status::Extended
def text
'manual'
end
def label
'manual play action'
end
def icon
'icon_status_manual'
end
def has_action?
can?(user, :update_build, subject)
end
def action_icon
'play'
end
def action_title
'Play'
end
def action_class
'ci-play-icon'
end
def action_path
play_namespace_project_build_path(subject.project.namespace,
subject.project,
subject)
end
def action_method
:post
end
def self.matches?(build, user)
build.playable? && !build.stops_environment?
end
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Gitlab
module Ci
module Status
module Build
class Retryable < SimpleDelegator
include Status::Extended
def has_action?
can?(user, :update_build, subject)
end
def action_icon
'refresh'
end
def action_title
'Retry'
end
def action_path
retry_namespace_project_build_path(subject.project.namespace,
subject.project,
subject)
end
def action_method
:post
end
def self.matches?(build, user)
build.retryable?
end
end
end
end
end
end

View file

@ -0,0 +1,49 @@
module Gitlab
module Ci
module Status
module Build
class Stop < SimpleDelegator
include Status::Extended
def text
'manual'
end
def label
'manual stop action'
end
def icon
'icon_status_manual'
end
def has_action?
can?(user, :update_build, subject)
end
def action_icon
'stop'
end
def action_title
'Stop'
end
def action_path
play_namespace_project_build_path(subject.project.namespace,
subject.project,
subject)
end
def action_method
:post
end
def self.matches?(build, user)
build.playable? && build.stops_environment?
end
end
end
end
end
end

View file

@ -4,10 +4,14 @@ module Gitlab
# Base abstract class fore core status
#
class Core
include Gitlab::Routing.url_helpers
include Gitlab::Routing
include Gitlab::Allowable
def initialize(subject)
attr_reader :subject, :user
def initialize(subject, user)
@subject = subject
@user = user
end
def icon
@ -18,10 +22,6 @@ module Gitlab
raise NotImplementedError
end
def title
"#{@subject.class.name.demodulize}: #{label}"
end
# Deprecation warning: this method is here because we need to maintain
# backwards compatibility with legacy statuses. We often do something
# like "ci-status ci-status-#{status}" to set CSS class.
@ -34,7 +34,7 @@ module Gitlab
end
def has_details?
raise NotImplementedError
false
end
def details_path
@ -42,16 +42,27 @@ module Gitlab
end
def has_action?
raise NotImplementedError
false
end
def action_icon
raise NotImplementedError
end
def action_class
end
def action_path
raise NotImplementedError
end
def action_method
raise NotImplementedError
end
def action_title
raise NotImplementedError
end
end
end
end

View file

@ -2,10 +2,14 @@ module Gitlab
module Ci
module Status
module Extended
def matches?(_subject)
extend ActiveSupport::Concern
class_methods do
def matches?(_subject, _user)
raise NotImplementedError
end
end
end
end
end
end

View file

@ -2,10 +2,9 @@ module Gitlab
module Ci
module Status
class Factory
attr_reader :subject
def initialize(subject)
def initialize(subject, user)
@subject = subject
@user = user
end
def fabricate!
@ -16,27 +15,32 @@ module Gitlab
end
end
def self.extended_statuses
[]
end
def self.common_helpers
Module.new
end
private
def subject_status
@subject_status ||= subject.status
def simple_status
@simple_status ||= @subject.status || :created
end
def core_status
Gitlab::Ci::Status
.const_get(subject_status.capitalize)
.new(subject)
.const_get(simple_status.capitalize)
.new(@subject, @user)
.extend(self.class.common_helpers)
end
def extended_status
@extended ||= extended_statuses.find do |status|
status.matches?(subject)
@extended ||= self.class.extended_statuses.find do |status|
status.matches?(@subject, @user)
end
end
def extended_statuses
[]
end
end
end
end

View file

@ -4,13 +4,13 @@ module Gitlab
module Pipeline
module Common
def has_details?
true
can?(user, :read_pipeline, subject)
end
def details_path
namespace_project_pipeline_path(@subject.project.namespace,
@subject.project,
@subject)
namespace_project_pipeline_path(subject.project.namespace,
subject.project,
subject)
end
def has_action?

View file

@ -3,14 +3,12 @@ module Gitlab
module Status
module Pipeline
class Factory < Status::Factory
private
def extended_statuses
def self.extended_statuses
[Pipeline::SuccessWithWarnings]
end
def core_status
super.extend(Status::Pipeline::Common)
def self.common_helpers
Status::Pipeline::Common
end
end
end

View file

@ -3,7 +3,7 @@ module Gitlab
module Status
module Pipeline
class SuccessWithWarnings < SimpleDelegator
extend Status::Extended
include Status::Extended
def text
'passed'
@ -21,7 +21,7 @@ module Gitlab
'success_with_warnings'
end
def self.matches?(pipeline)
def self.matches?(pipeline, user)
pipeline.success? && pipeline.has_warnings?
end
end

View file

@ -4,14 +4,14 @@ module Gitlab
module Stage
module Common
def has_details?
true
can?(user, :read_pipeline, subject.pipeline)
end
def details_path
namespace_project_pipeline_path(@subject.project.namespace,
@subject.project,
@subject.pipeline,
anchor: @subject.name)
namespace_project_pipeline_path(subject.project.namespace,
subject.project,
subject.pipeline,
anchor: subject.name)
end
def has_action?

View file

@ -3,10 +3,8 @@ module Gitlab
module Status
module Stage
class Factory < Status::Factory
private
def core_status
super.extend(Status::Stage::Common)
def self.common_helpers
Status::Stage::Common
end
end
end

View file

@ -1,5 +1,11 @@
module Gitlab
module Routing
extend ActiveSupport::Concern
included do
include Gitlab::Routing.url_helpers
end
# Returns the URL helpers Module.
#
# This method caches the output as Rails' "url_helpers" method creates an

View file

@ -12,12 +12,14 @@ FactoryGirl.define do
started_at 'Di 29. Okt 09:51:28 CET 2013'
finished_at 'Di 29. Okt 09:53:28 CET 2013'
commands 'ls -a'
options do
{
image: "ruby:2.1",
services: ["postgres"]
}
end
yaml_variables do
[
{ key: :DB_NAME, value: 'postgres', public: true }
@ -60,15 +62,20 @@ FactoryGirl.define do
end
trait :teardown_environment do
options do
{ environment: { action: 'stop' } }
end
environment 'staging'
options environment: { name: 'staging',
action: 'stop' }
end
trait :allowed_to_fail do
allow_failure true
end
trait :playable do
skipped
manual
end
after(:build) do |build, evaluator|
build.project = build.pipeline.project
end

View file

@ -0,0 +1,27 @@
require 'spec_helper'
describe Gitlab::Allowable do
subject do
Class.new.include(described_class).new
end
describe '#can?' do
let(:user) { create(:user) }
context 'when user is allowed to do something' do
let(:project) { create(:empty_project, :public) }
it 'reports correct ability to perform action' do
expect(subject.can?(user, :read_project, project)).to be true
end
end
context 'when user is not allowed to do something' do
let(:project) { create(:empty_project, :private) }
it 'reports correct ability to perform action' do
expect(subject.can?(user, :read_project, project)).to be false
end
end
end
end

View file

@ -0,0 +1,86 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Build::Cancelable 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 'does not override status icon' do
expect(status).to receive(:icon)
subject.icon
end
end
describe '#label' do
it 'does not override status label' do
expect(status).to receive(:label)
subject.label
end
end
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
describe '#has_action?' do
context 'when user is allowed to update build' do
before { build.project.team << [user, :developer] }
it { is_expected.to have_action }
end
context 'when user is not allowed to update build' do
it { is_expected.not_to have_action }
end
end
describe '#action_path' do
it { expect(subject.action_path).to include "#{build.id}/cancel" }
end
describe '#action_icon' do
it { expect(subject.action_icon).to eq 'ban' }
end
describe '#action_title' do
it { expect(subject.action_title).to eq 'Cancel' }
end
end
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is cancelable' do
let(:build) do
create(:ci_build, :running)
end
it 'is a correct match' do
expect(subject).to be true
end
end
context 'when build is not cancelable' do
let(:build) { create(:ci_build, :success) }
it 'does not match' do
expect(subject).to be false
end
end
end
end

View file

@ -0,0 +1,37 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Build::Common do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
let(:project) { build.project }
subject do
Gitlab::Ci::Status::Core
.new(build, user)
.extend(described_class)
end
describe '#has_action?' do
it { is_expected.not_to have_action }
end
describe '#has_details?' do
context 'when user has access to read build' do
before { project.team << [user, :developer] }
it { is_expected.to have_details }
end
context 'when user does not have access to read build' do
before { project.update(public_builds: false) }
it { is_expected.not_to have_details }
end
end
describe '#details_path' do
it 'links to the build details page' do
expect(subject.details_path).to include "builds/#{build.id}"
end
end
end

View file

@ -0,0 +1,141 @@
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! }
before { project.team << [user, :developer] }
context 'when build is successful' do
let(:build) { create(:ci_build, :success) }
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 'passed'
expect(status.icon).to eq 'icon_status_success'
expect(status.label).to eq 'passed'
expect(status).to have_details
expect(status).to have_action
end
end
context 'when build is failed' do
let(:build) { create(:ci_build, :failed) }
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
context 'when build is a canceled' do
let(:build) { create(:ci_build, :canceled) }
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 'canceled'
expect(status.icon).to eq 'icon_status_canceled'
expect(status.label).to eq 'canceled'
expect(status).to have_details
expect(status).to have_action
end
end
context 'when build is running' do
let(:build) { create(:ci_build, :running) }
it 'fabricates a canceable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'running'
expect(status.icon).to eq 'icon_status_running'
expect(status.label).to eq 'running'
expect(status).to have_details
expect(status).to have_action
end
end
context 'when build is pending' do
let(:build) { create(:ci_build, :pending) }
it 'fabricates a cancelable build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'pending'
expect(status.icon).to eq 'icon_status_pending'
expect(status.label).to eq 'pending'
expect(status).to have_details
expect(status).to have_action
end
end
context 'when build is skipped' do
let(:build) { create(:ci_build, :skipped) }
it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Skipped
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'skipped'
expect(status.icon).to eq 'icon_status_skipped'
expect(status.label).to eq 'skipped'
expect(status).to have_details
expect(status).not_to have_action
end
end
context 'when build is a manual action' do
context 'when build is a play action' do
let(:build) { create(:ci_build, :playable) }
it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Play
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'manual'
expect(status.icon).to eq 'icon_status_manual'
expect(status.label).to eq 'manual play action'
expect(status).to have_details
expect(status).to have_action
end
end
context 'when build is an environment stop action' do
let(:build) { create(:ci_build, :playable, :teardown_environment) }
it 'fabricates a core skipped status' do
expect(status).to be_a Gitlab::Ci::Status::Build::Stop
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'manual'
expect(status.icon).to eq 'icon_status_manual'
expect(status.label).to eq 'manual stop action'
expect(status).to have_details
expect(status).to have_action
end
end
end
end

View file

@ -0,0 +1,82 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Build::Play do
let(:status) { double('core') }
let(:user) { double('user') }
subject { described_class.new(status) }
describe '#text' do
it { expect(subject.text).to eq 'manual' }
end
describe '#label' do
it { expect(subject.label).to eq 'manual play action' }
end
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_manual' }
end
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
describe '#has_action?' do
context 'when user is allowed to update build' do
before { build.project.team << [user, :developer] }
it { is_expected.to have_action }
end
context 'when user is not allowed to update build' do
it { is_expected.not_to have_action }
end
end
describe '#action_path' do
it { expect(subject.action_path).to include "#{build.id}/play" }
end
describe '#action_icon' do
it { expect(subject.action_icon).to eq 'play' }
end
describe '#action_title' do
it { expect(subject.action_title).to eq 'Play' }
end
end
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is playable' do
context 'when build stops an environment' do
let(:build) do
create(:ci_build, :playable, :teardown_environment)
end
it 'does not match' do
expect(subject).to be false
end
end
context 'when build does not stop an environment' do
let(:build) { create(:ci_build, :playable) }
it 'is a correct match' do
expect(subject).to be true
end
end
end
context 'when build is not playable' do
let(:build) { create(:ci_build) }
it 'does not match' do
expect(subject).to be false
end
end
end
end

View file

@ -0,0 +1,86 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Build::Retryable 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 'does not override status icon' do
expect(status).to receive(:icon)
subject.icon
end
end
describe '#label' do
it 'does not override status label' do
expect(status).to receive(:label)
subject.label
end
end
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
describe '#has_action?' do
context 'when user is allowed to update build' do
before { build.project.team << [user, :developer] }
it { is_expected.to have_action }
end
context 'when user is not allowed to update build' do
it { is_expected.not_to have_action }
end
end
describe '#action_path' do
it { expect(subject.action_path).to include "#{build.id}/retry" }
end
describe '#action_icon' do
it { expect(subject.action_icon).to eq 'refresh' }
end
describe '#action_title' do
it { expect(subject.action_title).to eq 'Retry' }
end
end
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is retryable' do
let(:build) do
create(:ci_build, :success)
end
it 'is a correct match' do
expect(subject).to be true
end
end
context 'when build is not retryable' do
let(:build) { create(:ci_build, :running) }
it 'does not match' do
expect(subject).to be false
end
end
end
end

View file

@ -0,0 +1,84 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Build::Stop do
let(:status) { double('core status') }
let(:user) { double('user') }
subject do
described_class.new(status)
end
describe '#text' do
it { expect(subject.text).to eq 'manual' }
end
describe '#label' do
it { expect(subject.label).to eq 'manual stop action' }
end
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_manual' }
end
describe 'action details' do
let(:user) { create(:user) }
let(:build) { create(:ci_build) }
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
describe '#has_action?' do
context 'when user is allowed to update build' do
before { build.project.team << [user, :developer] }
it { is_expected.to have_action }
end
context 'when user is not allowed to update build' do
it { is_expected.not_to have_action }
end
end
describe '#action_path' do
it { expect(subject.action_path).to include "#{build.id}/play" }
end
describe '#action_icon' do
it { expect(subject.action_icon).to eq 'stop' }
end
describe '#action_title' do
it { expect(subject.action_title).to eq 'Stop' }
end
end
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is playable' do
context 'when build stops an environment' do
let(:build) do
create(:ci_build, :playable, :teardown_environment)
end
it 'is a correct match' do
expect(subject).to be true
end
end
context 'when build does not stop an environment' do
let(:build) { create(:ci_build, :playable) }
it 'does not match' do
expect(subject).to be false
end
end
end
context 'when build is not playable' do
let(:build) { create(:ci_build) }
it 'does not match' do
expect(subject).to be false
end
end
end
end

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Canceled do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'canceled' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Canceled do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_canceled' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: canceled' }
end
end

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Created do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'created' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Created do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_created' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: created' }
end
end

View file

@ -2,11 +2,11 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Extended do
subject do
Class.new.extend(described_class)
Class.new.include(described_class)
end
it 'requires subclass to implement matcher' do
expect { subject.matches?(double) }
expect { subject.matches?(double, double) }
.to raise_error(NotImplementedError)
end
end

View file

@ -2,15 +2,17 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Factory do
subject do
described_class.new(object)
described_class.new(resource, user)
end
let(:user) { create(:user) }
let(:status) { subject.fabricate! }
context 'when object has a core status' do
HasStatus::AVAILABLE_STATUSES.each do |core_status|
context "when core status is #{core_status}" do
let(:object) { double(status: core_status) }
let(:resource) { double(status: core_status) }
it "fabricates a core status #{core_status}" do
expect(status).to be_a(

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Failed do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'failed' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Failed do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_failed' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: failed' }
end
end

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Pending do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'pending' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Pending do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_pending' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: pending' }
end
end

View file

@ -1,23 +1,36 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::Common do
let(:pipeline) { create(:ci_pipeline) }
let(:user) { create(:user) }
let(:project) { create(:empty_project, :private) }
let(:pipeline) { create(:ci_pipeline, project: project) }
subject do
Class.new(Gitlab::Ci::Status::Core)
.new(pipeline).extend(described_class)
Gitlab::Ci::Status::Core
.new(pipeline, user)
.extend(described_class)
end
it 'does not have action' do
expect(subject).not_to have_action
describe '#has_action?' do
it { is_expected.not_to have_action }
end
it 'has details' do
expect(subject).to have_details
describe '#has_details?' do
context 'when user has access to read pipeline' do
before { project.team << [user, :developer] }
it { is_expected.to have_details }
end
context 'when user does not have access to read pipeline' do
it { is_expected.not_to have_details }
end
end
describe '#details_path' do
it 'links to the pipeline details page' do
expect(subject.details_path)
.to include "pipelines/#{pipeline.id}"
end
end
end

View file

@ -1,14 +1,21 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Pipeline::Factory do
let(:user) { create(:user) }
let(:project) { pipeline.project }
subject do
described_class.new(pipeline)
described_class.new(pipeline, user)
end
let(:status) do
subject.fabricate!
end
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

View file

@ -29,13 +29,13 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
end
it 'is a correct match' do
expect(described_class.matches?(pipeline)).to eq true
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)).to eq false
expect(described_class.matches?(pipeline, double)).to eq false
end
end
end
@ -51,13 +51,13 @@ describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
end
it 'does not match' do
expect(described_class.matches?(pipeline)).to eq false
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)).to eq false
expect(described_class.matches?(pipeline, double)).to eq false
end
end
end

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Running do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'running' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Running do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_running' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: running' }
end
end

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Skipped do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'skipped' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Skipped do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_skipped' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: skipped' }
end
end

View file

@ -1,26 +1,43 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Stage::Common do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') }
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:stage) do
build(:ci_stage, pipeline: pipeline, name: 'test')
end
subject do
Class.new(Gitlab::Ci::Status::Core)
.new(stage).extend(described_class)
.new(stage, user).extend(described_class)
end
it 'does not have action' do
expect(subject).not_to have_action
end
it 'has details' do
expect(subject).to have_details
end
it 'links to the pipeline details page' do
expect(subject.details_path)
.to include "pipelines/#{pipeline.id}"
expect(subject.details_path)
.to include "##{stage.name}"
end
context 'when user has permission to read pipeline' do
before do
project.team << [user, :master]
end
it 'has details' do
expect(subject).to have_details
end
end
context 'when user does not have permission to read pipeline' do
it 'does not have details' do
expect(subject).not_to have_details
end
end
end

View file

@ -1,17 +1,26 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Stage::Factory do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') }
let(:user) { create(:user) }
let(:project) { create(:empty_project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:stage) do
build(:ci_stage, pipeline: pipeline, name: 'test')
end
subject do
described_class.new(stage)
described_class.new(stage, user)
end
let(:status) do
subject.fabricate!
end
before do
project.team << [user, :developer]
end
context 'when stage has a core status' do
HasStatus::AVAILABLE_STATUSES.each do |core_status|
context "when core status is #{core_status}" do

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Gitlab::Ci::Status::Success do
subject { described_class.new(double('subject')) }
subject do
described_class.new(double('subject'), double('user'))
end
describe '#text' do
it { expect(subject.label).to eq 'passed' }
@ -14,8 +16,4 @@ describe Gitlab::Ci::Status::Success do
describe '#icon' do
it { expect(subject.icon).to eq 'icon_status_success' }
end
describe '#title' do
it { expect(subject.title).to eq 'Double: passed' }
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe Gitlab::Routing do
context 'when module is included' do
subject do
Class.new.include(described_class).new
end
it 'makes it possible to access url helpers' do
expect(subject).to respond_to(:namespace_project_path)
end
end
context 'when module is not included' do
subject do
Class.new.include(described_class.url_helpers).new
end
it 'exposes url helpers module through a method' do
expect(subject).to respond_to(:namespace_project_path)
end
end
end

View file

@ -899,21 +899,87 @@ describe Ci::Build, models: true do
end
end
describe '#retryable?' do
describe '#cancelable?' do
subject { build }
context 'when build is cancelable' do
context 'when build is pending' do
it { is_expected.to be_cancelable }
end
context 'when build is running' do
before do
build.run!
end
it { expect(build).not_to be_retryable }
it { is_expected.to be_cancelable }
end
end
context 'when build is finished' do
context 'when build is not cancelable' do
context 'when build is successful' do
before do
build.success!
end
it { expect(build).to be_retryable }
it { is_expected.not_to be_cancelable }
end
context 'when build is failed' do
before do
build.drop!
end
it { is_expected.not_to be_cancelable }
end
end
end
describe '#retryable?' do
subject { build }
context 'when build is retryable' do
context 'when build is successful' do
before do
build.success!
end
it { is_expected.to be_retryable }
end
context 'when build is failed' do
before do
build.drop!
end
it { is_expected.to be_retryable }
end
context 'when build is canceled' do
before do
build.cancel!
end
it { is_expected.to be_retryable }
end
end
context 'when build is not retryable' do
context 'when build is running' do
before do
build.run!
end
it { is_expected.not_to be_retryable }
end
context 'when build is skipped' do
before do
build.skip!
end
it { is_expected.not_to be_retryable }
end
end
end
@ -1180,4 +1246,13 @@ describe Ci::Build, models: true do
it { is_expected.to eq('review/master') }
end
end
describe '#detailed_status' do
let(:user) { create(:user) }
it 'returns a detailed status' do
expect(build.detailed_status(user))
.to be_a Gitlab::Ci::Status::Build::Cancelable
end
end
end

View file

@ -442,11 +442,15 @@ describe Ci::Pipeline, models: true do
end
describe '#detailed_status' do
let(:user) { create(:user) }
subject { pipeline.detailed_status(user) }
context 'when pipeline is created' do
let(:pipeline) { create(:ci_pipeline, status: :created) }
it 'returns detailed status for created pipeline' do
expect(pipeline.detailed_status.text).to eq 'created'
expect(subject.text).to eq 'created'
end
end
@ -454,7 +458,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :pending) }
it 'returns detailed status for pending pipeline' do
expect(pipeline.detailed_status.text).to eq 'pending'
expect(subject.text).to eq 'pending'
end
end
@ -462,7 +466,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :running) }
it 'returns detailed status for running pipeline' do
expect(pipeline.detailed_status.text).to eq 'running'
expect(subject.text).to eq 'running'
end
end
@ -470,7 +474,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :success) }
it 'returns detailed status for successful pipeline' do
expect(pipeline.detailed_status.text).to eq 'passed'
expect(subject.text).to eq 'passed'
end
end
@ -478,7 +482,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :failed) }
it 'returns detailed status for failed pipeline' do
expect(pipeline.detailed_status.text).to eq 'failed'
expect(subject.text).to eq 'failed'
end
end
@ -486,7 +490,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :canceled) }
it 'returns detailed status for canceled pipeline' do
expect(pipeline.detailed_status.text).to eq 'canceled'
expect(subject.text).to eq 'canceled'
end
end
@ -494,7 +498,7 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { create(:ci_pipeline, status: :skipped) }
it 'returns detailed status for skipped pipeline' do
expect(pipeline.detailed_status.text).to eq 'skipped'
expect(subject.text).to eq 'skipped'
end
end
@ -506,7 +510,7 @@ describe Ci::Pipeline, models: true do
end
it 'retruns detailed status for successful pipeline with warnings' do
expect(pipeline.detailed_status.label).to eq 'passed with warnings'
expect(subject.label).to eq 'passed with warnings'
end
end
end

View file

@ -68,7 +68,9 @@ describe Ci::Stage, models: true do
end
describe '#detailed_status' do
subject { stage.detailed_status }
let(:user) { create(:user) }
subject { stage.detailed_status(user) }
context 'when build is created' do
let!(:stage_build) { create_job(:ci_build, status: :created) }

View file

@ -234,4 +234,13 @@ describe CommitStatus, models: true do
end
end
end
describe '#detailed_status' do
let(:user) { create(:user) }
it 'returns a detailed status' do
expect(commit_status.detailed_status(user))
.to be_a Gitlab::Ci::Status::Success
end
end
end

View file

@ -1,8 +1,11 @@
require 'spec_helper'
describe GenericCommitStatus, models: true do
let(:pipeline) { FactoryGirl.create :ci_pipeline }
let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline }
let(:pipeline) { create(:ci_pipeline) }
let(:generic_commit_status) do
create(:generic_commit_status, pipeline: pipeline)
end
describe '#context' do
subject { generic_commit_status.context }
@ -17,6 +20,15 @@ describe GenericCommitStatus, models: true do
it { is_expected.to eq([:external]) }
end
describe '#detailed_status' do
let(:user) { create(:user) }
it 'returns detailed status object' do
expect(generic_commit_status.detailed_status(user))
.to be_a Gitlab::Ci::Status::Success
end
end
describe 'set_default_values' do
before do
generic_commit_status.context = nil