diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 11adf2568a1..889366d6ddf 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -98,7 +98,7 @@
@extend .dropdown-toggle;
padding-right: 20px;
position: relative;
- width: 160px;
+ width: 163px;
text-overflow: ellipsis;
overflow: hidden;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 9a4685504ff..267fcd77b38 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -524,3 +524,9 @@ $body-text-shadow: rgba(255,255,255,0.01);
*/
$ui-dev-kit-example-color: #bbb;
$ui-dev-kit-example-border: #ddd;
+
+/*
+Pipeline Graph
+*/
+$stage-hover-bg: #eaf3fc;
+$stage-hover-border: #d1e7fc;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index d5f9a7088be..be22e7bdc79 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -288,15 +288,40 @@
}
// Pipeline visualization
+.pipeline-actions {
+ border-bottom: none;
+}
-.toggle-pipeline-btn {
- background-color: $white-normal;
+.tab-pane {
+ &.pipelines {
+ .ci-table {
+ min-width: 900px;
+ }
- &.graph-collapsed {
- background-color: $white-light;
+ .content-list.pipelines {
+ overflow: auto;
+ }
+
+ .stage {
+ max-width: 100px;
+ width: 100px;
+ }
+
+ .pipeline-actions {
+ min-width: initial;
+ }
+ }
+
+ &.builds {
+ .ci-table {
+ tr {
+ height: 71px;
+ }
+ }
}
}
+// Pipeline graph
.pipeline-graph {
width: 100%;
background-color: $gray-light;
@@ -305,52 +330,120 @@
white-space: nowrap;
transition: max-height 0.3s, padding 0.3s;
- &.graph-collapsed {
- max-height: 0;
- padding: 0 16px;
- }
-}
-
-.pipeline-visualization {
- position: relative;
-
ul {
padding: 0;
}
-}
-.stage-column {
- display: inline-block;
- vertical-align: top;
-
- &:not(:last-child) {
- margin-right: 44px;
+ a {
+ text-decoration: none;
+ color: $gl-text-color-light;
}
- &.left-margin {
- &:not(:first-child) {
- margin-left: 44px;
+ svg {
+ vertical-align: middle;
+ margin-right: 3px;
+ }
- .left-connector {
- &::before {
- content: '';
- position: absolute;
- top: 48%;
- left: -48px;
- border-top: 2px solid $border-color;
- width: 48px;
- height: 1px;
+ .stage-column {
+ display: inline-block;
+ vertical-align: top;
+
+ &:not(:last-child) {
+ margin-right: 44px;
+ }
+
+ &.left-margin {
+ &:not(:first-child) {
+ margin-left: 44px;
+
+ .left-connector {
+ &::before {
+ content: '';
+ position: absolute;
+ top: 48%;
+ left: -48px;
+ border-top: 2px solid $border-color;
+ width: 48px;
+ height: 1px;
+ }
}
}
}
- }
- &.no-margin {
- margin: 0;
- }
+ &.no-margin {
+ margin: 0;
+ }
- li {
- list-style: none;
+ li {
+ list-style: none;
+ }
+
+ &:last-child {
+ .build {
+ // Remove right connecting horizontal line from first build in last stage
+ &:first-child {
+ &::after {
+ border: none;
+ }
+ }
+ // Remove right curved connectors from all builds in last stage
+ &:not(:first-child) {
+ &::after {
+ border: none;
+ }
+ }
+ // Remove opposite curve
+ .curve {
+ &::before {
+ display: none;
+ }
+ }
+ }
+ }
+
+ &:first-child {
+ .build {
+ // Remove left curved connectors from all builds in first stage
+ &:not(:first-child) {
+ &::before {
+ border: none;
+ }
+ }
+ // Remove opposite curve
+ .curve {
+ &::after {
+ display: none;
+ }
+ }
+ }
+ }
+
+ // Curve first child connecting lines in opposite direction
+ .curve {
+ display: none;
+
+ &::before,
+ &::after {
+ content: '';
+ width: 21px;
+ height: 25px;
+ position: absolute;
+ top: -32px;
+ border-top: 2px solid $border-color;
+ }
+
+ &::after {
+ left: -44px;
+ border-right: 2px solid $border-color;
+ border-radius: 0 20px;
+ }
+
+ &::before {
+ right: -44px;
+ border-left: 2px solid $border-color;
+ border-radius: 20px 0 0;
+ }
+ }
}
.stage-name {
@@ -364,166 +457,69 @@
.build {
border: 1px solid $border-color;
+ border-radius: 30px;
background-color: $white-light;
position: relative;
- padding: 7px 10px 8px;
- border-radius: 30px;
+ padding: 8px 4px 9px 10px;
width: 186px;
margin-bottom: 10px;
+ white-space: normal;
&:hover {
- background-color: $gray-lighter;
- }
-
- &.playable {
-
- svg {
- height: 13px;
- width: 20px;
- position: relative;
- top: 1px;
-
- path {
- fill: $layout-link-gray;
- }
- }
- }
-
- .build-content {
- display: -ms-flexbox;
- display: -webkit-flex;
- display: flex;
- width: 164px;
-
- .ci-status-icon {
- svg {
- height: 20px;
- width: 20px;
- }
- }
-
- .tooltip {
- white-space: nowrap;
-
- .tooltip-inner {
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
-
- .ci-status-text {
- width: 135px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- vertical-align: middle;
- display: inline-block;
- position: relative;
- top: -1px;
- }
-
- a {
- color: $gl-text-color-light;
- text-decoration: none;
- }
+ background-color: $stage-hover-bg;
+ border: 1px solid $stage-hover-border;
+ a,
+ .dropdown-counter-badge,
.dropdown-menu-toggle {
- background-color: transparent;
- border: none;
- width: auto;
- padding: 0;
- color: $gl-text-color-light;
- flex-grow: 1;
-
- .ci-status-text {
- max-width: 112px;
- width: auto;
- }
+ color: $gl-text-color;
}
- .grouped-pipeline-dropdown {
- padding: 0;
- width: 186px;
- left: auto;
- right: -197px;
- top: -9px;
+ .grouped-pipeline-dropdown a {
+ color: $gl-text-color-light;
- ul {
- max-height: 245px;
- overflow: auto;
-
- li:first-child {
- padding-top: 8px;
- }
-
- li:last-child {
- padding-bottom: 8px;
- }
- }
-
- a {
+ &:hover {
color: $gl-text-color;
- padding: 7px 8px 8px;
-
- &:hover {
- background-color: $blue-light-transparent;
- border-radius: 3px;
-
- .ci-status-text {
- text-decoration: none;
- }
- }
}
-
- svg {
- width: 14px;
- height: 14px;
- }
-
- .ci-status-text {
- width: 112px;
- }
-
- .arrow {
- &::before,
- &::after {
- content: '';
- display: inline-block;
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
- top: 18px;
- }
-
- &::before {
- left: -5px;
- margin-top: -6px;
- border-width: 7px 5px 7px 0;
- border-right-color: $border-color;
- }
-
- &::after {
- left: -4px;
- margin-top: -9px;
- border-width: 10px 7px 10px 0;
- border-right-color: $white-light;
- }
- }
- }
-
- .badge {
- background-color: $gray-darker;
- color: $gl-text-color-light;
- font-weight: normal;
- margin-left: $btn-xs-side-margin;
}
}
- svg {
- vertical-align: middle;
- margin-right: 5px;
+ .ci-status-icon {
+ position: relative;
+ top: 1px;
+ }
+
+ .ci-status-icon svg {
+ height: 20px;
+ width: 20px;
+ }
+
+ .arrow {
+ &::before,
+ &::after {
+ content: '';
+ display: inline-block;
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+ top: 18px;
+ }
+
+ &::before {
+ left: -5px;
+ margin-top: -6px;
+ border-width: 7px 5px 7px 0;
+ border-right-color: $border-color;
+ }
+
+ &::after {
+ left: -4px;
+ margin-top: -9px;
+ border-width: 10px 7px 10px 0;
+ border-right-color: $white-light;
+ }
}
// Connect first build in each stage with right horizontal line
@@ -532,7 +528,7 @@
content: '';
position: absolute;
top: 48%;
- right: -48px;
+ right: -49px;
border-top: 2px solid $border-color;
width: 48px;
height: 1px;
@@ -580,109 +576,161 @@
}
}
- &:last-child {
- .build {
- // Remove right connecting horizontal line from first build in last stage
- &:first-child {
- &::after {
- border: none;
- }
- }
- // Remove right curved connectors from all builds in last stage
- &:not(:first-child) {
- &::after {
- border: none;
- }
- }
- // Remove opposite curve
- .curve {
- &::before {
- display: none;
- }
+ .ci-status-text {
+ max-width: 110px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: bottom;
+ display: inline-block;
+ position: relative;
+ font-weight: 100;
+ }
+
+ .dropdown-menu-toggle {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ color: $gl-text-color-light;
+ white-space: normal;
+ overflow: visible;
+
+ &:focus {
+ outline: none;
+ }
+
+ &:hover {
+ color: $gl-text-color;
+
+ .dropdown-counter-badge {
+ color: $gl-text-color;
}
}
}
- &:first-child {
- .build {
- // Remove left curved connectors from all builds in first stage
- &:not(:first-child) {
- &::before {
- border: none;
+ .dropdown-counter-badge {
+ float: right;
+ clear: right;
+ color: $border-color;
+ font-weight: 100;
+ font-size: 15px;
+ margin-right: 2px;
+ }
+
+ .grouped-pipeline-dropdown {
+ padding: 0;
+ width: 191px;
+ left: auto;
+ right: -206px;
+ top: -11px;
+ box-shadow: 0 1px 5px $black-transparent;
+
+ a {
+ display: inline-block;
+
+ &:hover {
+ background-color: $stage-hover-bg;
+ }
+ }
+
+ ul {
+ max-height: 245px;
+ overflow: auto;
+ margin: 5px 0;
+
+ li {
+ margin: 0 5px;
+ padding-left: 0;
+ padding-bottom: 0;
+ margin-bottom: 0;
+ line-height: 1.2;
+ }
+ }
+
+ .dropdown-build {
+ color: $gl-text-color-light;
+
+ a.ci-action-icon-container {
+ padding: 0;
+ font-size: 11px;
+ float: right;
+ margin-top: 4px;
+ display: inline-block;
+ position: relative;
+
+ i {
+ font-size: 11px;
+ margin-top: 0;
}
}
- // Remove opposite curve
- .curve {
- &::after {
- display: none;
+
+ &:hover {
+ background-color: $stage-hover-bg;
+ border-radius: 3px;
+ color: $gl-text-color;
+ }
+
+ .ci-action-icon-container {
+ i {
+ width: 25px;
+ height: 25px;
+
+ &::before {
+ top: 1px;
+ left: 1px;
+ }
}
}
+
+ .stage {
+ max-width: 100px;
+ width: 100px;
+ }
+
+ .ci-status-icon svg {
+ height: 18px;
+ width: 18px;
+ }
+
+ .ci-status-text {
+ max-width: 95px;
+ padding-bottom: 3px;
+ position: relative;
+ top: 3px;
+ }
}
}
+}
- // Curve first child connecting lines in opposite direction
- .curve {
- display: none;
+// Action Icons
+.ci-action-icon-container .ci-action-icon-wrapper {
+ float: right;
+ margin-top: -4px;
- &::before,
- &::after {
- content: '';
- width: 21px;
- height: 25px;
- position: absolute;
- top: -32px;
- border-top: 2px solid $border-color;
- }
-
- &::after {
- left: -44px;
- border-right: 2px solid $border-color;
- border-radius: 0 20px;
- }
+ i {
+ color: $border-color;
+ border-radius: 100%;
+ border: 1px solid $border-color;
+ padding: 5px 6px;
+ font-size: 13px;
+ background: $white-light;
+ height: 30px;
+ width: 30px;
&::before {
- right: -44px;
- border-left: 2px solid $border-color;
- border-radius: 20px 0 0;
- }
- }
-}
-
-.pipeline-actions {
- border-bottom: none;
-}
-
-.toggle-pipeline-btn {
-
- .fa {
- color: $gl-gray-light;
- }
-}
-
-.tab-pane {
-
- &.pipelines {
-
- .ci-table {
- min-width: 900px;
+ position: relative;
+ top: 3px;
+ left: 3px;
}
- .stage {
- max-width: 100px;
- width: 100px;
- }
-
- .pipeline-actions {
- min-width: initial;
+ &:hover {
+ color: $gl-text-color;
+ background-color: $stage-hover-bg;
+ border: 1px solid $stage-hover-bg;
}
}
- &.builds {
-
- .ci-table {
- tr {
- height: 71px;
- }
- }
+ .ci-play-icon {
+ padding: 5px 5px 5px 7px;
}
}
diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml
new file mode 100644
index 00000000000..c7d04ab61e9
--- /dev/null
+++ b/app/views/ci/status/_graph_badge.html.haml
@@ -0,0 +1,19 @@
+-# Renders the graph node with both the status icon, status name and action icon
+
+- subject = local_assigns.fetch(:subject)
+- status = subject.detailed_status(current_user)
+- klass = "ci-status-icon ci-status-icon-#{status}"
+
+- if status.has_details?
+ = link_to status.details_path, data: { toggle: 'tooltip', title: "#{subject.name} - #{status.label}" } do
+ %span{ class: klass }= custom_icon(status.icon)
+ .ci-status-text= subject.name
+- else
+ %span{ class: klass }= custom_icon(status.icon)
+ .ci-status-text= subject.name
+
+- if status.has_action?
+ = link_to status.action_path, method: status.action_method,
+ title: status.action_title, class: 'ci-action-icon-container' do
+ %i.ci-action-icon-wrapper
+ = icon(status.action_icon, class: status.action_class)
diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml
deleted file mode 100644
index ad1a7360a8b..00000000000
--- a/app/views/projects/ci/builds/_build_pipeline.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-- is_playable = subject.playable? && can?(current_user, :update_build, @project)
-- if is_playable
- = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.js-pipeline-graph', placement: 'bottom' } do
- = ci_icon_for_status('play')
- .ci-status-text= subject.name
-- elsif can?(current_user, :read_build, @project)
- = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.js-pipeline-graph', placement: 'bottom' } do
- %span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
- = ci_icon_for_status(subject.status)
- .ci-status-text= subject.name
-- else
- %span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
- = ci_icon_for_status(subject.status)
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml
deleted file mode 100644
index 1bba0443154..00000000000
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-%a{ data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.js-pipeline-graph', placement: 'bottom' } }
- - if subject.target_url
- = link_to subject.target_url do
- %span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
- = ci_icon_for_status(subject.status)
- %span.ci-status-text= subject.name
- - else
- %span{class: "ci-status-icon ci-status-icon-#{subject.status}"}
- = ci_icon_for_status(subject.status)
- %span.ci-status-text= subject.name
diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml
index bf8c75b6e5c..b70b574e687 100644
--- a/app/views/projects/stage/_graph.html.haml
+++ b/app/views/projects/stage/_graph.html.haml
@@ -10,11 +10,10 @@
- status_groups.each do |group_name, grouped_statuses|
- if grouped_statuses.one?
- status = grouped_statuses.first
- - is_playable = status.playable? && can?(current_user, :update_build, @project)
- %li.build{ class: ("playable" if is_playable) }
+ %li.build
.curve
.build-content
- = render "projects/#{status.to_partial_path}_pipeline", subject: status
+ = render 'ci/status/graph_badge', subject: status
- else
%li.build
.curve
diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml
index 2b26ad9d6fa..b03837d1211 100644
--- a/app/views/projects/stage/_in_stage_group.html.haml
+++ b/app/views/projects/stage/_in_stage_group.html.haml
@@ -4,10 +4,10 @@
= ci_icon_for_status(group_status)
%span.ci-status-text
= name
- %span.badge= subject.size
+ %span.dropdown-counter-badge= subject.size
.dropdown-menu.grouped-pipeline-dropdown
.arrow
%ul
- subject.each do |status|
- %li
- = render "projects/#{status.to_partial_path}_pipeline", subject: status
+ %li.dropdown-build
+ = render 'ci/status/graph_badge', subject: status
diff --git a/app/views/shared/icons/_icon_status_canceled.svg b/app/views/shared/icons/_icon_status_canceled.svg
old mode 100644
new mode 100755
index 41a210a8ed9..bd5d04e1cd7
--- a/app/views/shared/icons/_icon_status_canceled.svg
+++ b/app/views/shared/icons/_icon_status_canceled.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_created.svg b/app/views/shared/icons/_icon_status_created.svg
old mode 100644
new mode 100755
index 1f5c3b51b03..326ad04e017
--- a/app/views/shared/icons/_icon_status_created.svg
+++ b/app/views/shared/icons/_icon_status_created.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg
old mode 100644
new mode 100755
index af267b8938a..64da5aa31fc
--- a/app/views/shared/icons/_icon_status_failed.svg
+++ b/app/views/shared/icons/_icon_status_failed.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg
old mode 100644
new mode 100755
index 516231d1b44..02d5da407e3
--- a/app/views/shared/icons/_icon_status_pending.svg
+++ b/app/views/shared/icons/_icon_status_pending.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg
old mode 100644
new mode 100755
index d2618bce200..532f4fee33c
--- a/app/views/shared/icons/_icon_status_running.svg
+++ b/app/views/shared/icons/_icon_status_running.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg
old mode 100644
new mode 100755
index 701f33bcbea..1998dfef9ea
--- a/app/views/shared/icons/_icon_status_skipped.svg
+++ b/app/views/shared/icons/_icon_status_skipped.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg
old mode 100644
new mode 100755
index b7c21ba6971..eed5006bebe
--- a/app/views/shared/icons/_icon_status_success.svg
+++ b/app/views/shared/icons/_icon_status_success.svg
@@ -1 +1 @@
-
+
diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg
old mode 100644
new mode 100755
index 9191e0050a6..cb785635b7e
--- a/app/views/shared/icons/_icon_status_warning.svg
+++ b/app/views/shared/icons/_icon_status_warning.svg
@@ -1 +1 @@
-
+
diff --git a/changelogs/unreleased/22604-manual-actions.yml b/changelogs/unreleased/22604-manual-actions.yml
new file mode 100644
index 00000000000..7335e597292
--- /dev/null
+++ b/changelogs/unreleased/22604-manual-actions.yml
@@ -0,0 +1,4 @@
+---
+title: Resolve "Manual actions on pipeline graph"
+merge_request: 7931
+author:
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 23a504ff965..8f561c8f90b 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -107,7 +107,7 @@ describe 'Commits' do
describe 'Cancel build' do
it 'cancels build' do
visit ci_status_path(pipeline)
- click_on 'Cancel'
+ find('a.btn[title="Cancel"]').click
expect(page).to have_content 'canceled'
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 3350a3aeefc..0a77eaa123c 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -38,6 +38,91 @@ describe "Pipelines", feature: true, js: true do
expect(page).to have_css('#js-tab-pipeline.active')
end
+ describe 'pipeline graph' do
+ context 'when pipeline has running builds' do
+ it 'shows a running icon and a cancel action for the running build' do
+ page.within('a[data-title="deploy - running"]') do
+ expect(page).to have_selector('.ci-status-icon-running')
+ expect(page).to have_content('deploy')
+ end
+
+ page.within('a[data-title="deploy - running"] + .ci-action-icon-container') do
+ expect(page).to have_selector('.ci-action-icon-container .fa-ban')
+ end
+ end
+
+ it 'should be possible to cancel the running build' do
+ find('a[data-title="deploy - running"] + .ci-action-icon-container').trigger('click')
+
+ expect(page).not_to have_content('Cancel running')
+ end
+ end
+
+ context 'when pipeline has successful builds' do
+ it 'shows the success icon and a retry action for the successfull build' do
+ page.within('a[data-title="build - passed"]') do
+ expect(page).to have_selector('.ci-status-icon-success')
+ expect(page).to have_content('build')
+ end
+
+ page.within('a[data-title="build - passed"] + .ci-action-icon-container') do
+ expect(page).to have_selector('.ci-action-icon-container .fa-refresh')
+ end
+ end
+
+ it 'should be possible to retry the success build' do
+ find('a[data-title="build - passed"] + .ci-action-icon-container').trigger('click')
+
+ expect(page).not_to have_content('Retry build')
+ end
+ end
+
+ context 'when pipeline has failed builds' do
+ it 'shows the failed icon and a retry action for the failed build' do
+ page.within('a[data-title="test - failed"]') do
+ expect(page).to have_selector('.ci-status-icon-failed')
+ expect(page).to have_content('test')
+ end
+
+ page.within('a[data-title="test - failed"] + .ci-action-icon-container') do
+ expect(page).to have_selector('.ci-action-icon-container .fa-refresh')
+ end
+ end
+
+ it 'should be possible to retry the failed build' do
+ find('a[data-title="test - failed"] + .ci-action-icon-container').trigger('click')
+
+ expect(page).not_to have_content('Retry build')
+ end
+ end
+
+ context 'when pipeline has manual builds' do
+ it 'shows the skipped icon and a play action for the manual build' do
+ page.within('a[data-title="manual build - manual play action"]') do
+ expect(page).to have_selector('.ci-status-icon-skipped')
+ expect(page).to have_content('manual')
+ end
+
+ page.within('a[data-title="manual build - manual play action"] + .ci-action-icon-container') do
+ expect(page).to have_selector('.ci-action-icon-container .fa-play')
+ end
+ end
+
+ it 'should be possible to play the manual build' do
+ find('a[data-title="manual build - manual play action"] + .ci-action-icon-container').trigger('click')
+
+ expect(page).not_to have_content('Play build')
+ end
+ end
+
+ context 'when pipeline has external build' do
+ it 'shows the success icon and the generic comit status build' do
+ expect(page).to have_selector('.ci-status-icon-success')
+ expect(page).to have_content('jenkins')
+ end
+ end
+ end
+
context 'page tabs' do
it 'shows Pipeline and Builds tabs with link' do
expect(page).to have_link('Pipeline')