From d459b050c4e0a52e5c3ef32381b9a07147fc49a2 Mon Sep 17 00:00:00 2001
From: Eric Eastwood <contact@ericeastwood.com>
Date: Fri, 2 Feb 2018 15:50:09 -0600
Subject: [PATCH] Add Auto DevOps and Kubernetes cluster button to project page

---
 app/assets/stylesheets/framework/buttons.scss |  13 +
 app/assets/stylesheets/framework/mobile.scss  |   4 -
 app/assets/stylesheets/framework/sidebar.scss |   1 -
 .../stylesheets/framework/typography.scss     |   2 +-
 .../stylesheets/framework/variables.scss      |   6 +-
 app/assets/stylesheets/pages/projects.scss    |  53 +--
 app/helpers/projects_helper.rb                | 214 ++++++++++++
 .../projects/_stat_anchor_list.html.haml      |   5 +
 .../projects/_stat_anchor_list_item.html.haml |   7 +
 app/views/projects/empty.html.haml            |  53 +--
 app/views/projects/show.html.haml             |  60 +---
 ...devops-and-clusters-button-to-projects.yml |   6 +
 spec/features/auto_deploy_spec.rb             |  77 -----
 ...eate_license_file_in_empty_project_spec.rb |   2 +-
 spec/features/projects/show_project_spec.rb   | 317 ++++++++++++++++++
 spec/features/tags/master_views_tags_spec.rb  |   2 +-
 16 files changed, 627 insertions(+), 195 deletions(-)
 create mode 100644 app/views/projects/_stat_anchor_list.html.haml
 create mode 100644 app/views/projects/_stat_anchor_list_item.html.haml
 create mode 100644 changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml
 delete mode 100644 spec/features/auto_deploy_spec.rb

diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index c4b046a6d68..6b89387ab5f 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -444,6 +444,19 @@
   }
 }
 
+.btn-missing {
+  color: $notes-light-color;
+  border: 1px dashed $border-gray-normal-dashed;
+  border-radius: $border-radius-default;
+
+  &:hover,
+  &:active,
+  &:focus {
+    color: $notes-light-color;
+    background-color: $white-normal;
+  }
+}
+
 .btn-svg svg {
   @include btn-svg;
 }
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index a12f28efce6..8604e753c18 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -63,10 +63,6 @@
     }
   }
 
-  .project-stats {
-    display: none;
-  }
-
   .group-buttons {
     display: none;
   }
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index d61809cb0a4..d1d98270ad9 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -3,7 +3,6 @@
   transition: padding $sidebar-transition-duration;
 
   .container-fluid {
-    background: $white-light;
     padding: 0 $gl-padding;
 
     &.container-blank {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index d0999e60e65..fef5a1f51fa 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -296,7 +296,7 @@ body {
   line-height: 1.3;
   font-size: 1.25em;
   font-weight: $gl-font-weight-bold;
-  margin: 12px 7px;
+  margin: 12px 0;
 }
 
 h1,
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index f7f65d0e957..e11ab01ba7e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -216,8 +216,8 @@ $tooltip-font-size: 12px;
  */
 $gl-padding: 16px;
 $gl-padding-8: 8px;
+$gl-padding-4: 4px;
 $gl-col-padding: 15px;
-$gl-btn-padding: 10px;
 $gl-input-padding: 10px;
 $gl-vert-padding: 6px;
 $gl-padding-top: 10px;
@@ -384,6 +384,10 @@ $inactive-badge-background: rgba(0, 0, 0, .08);
 $btn-active-gray: #ececec;
 $btn-active-gray-light: e4e7ed;
 $btn-white-active: #848484;
+$gl-btn-padding: 10px;
+$gl-btn-line-height: 16px;
+$gl-btn-vert-padding: 8px;
+$gl-btn-horz-padding: 12px;
 
 /*
 * Badges
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 427ef45d016..a702fd25365 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -685,6 +685,9 @@ a.deploy-project-label {
   }
 }
 
+.project-empty-note-panel {
+  border-bottom: 1px solid $border-color;
+}
 
 .project-stats {
   font-size: 0;
@@ -693,11 +696,13 @@ a.deploy-project-label {
   border-bottom: 1px solid $border-color;
 
   .nav {
-    padding-top: 12px;
-    padding-bottom: 12px;
+    margin-top: $gl-padding-8;
+    margin-bottom: $gl-padding-8;
 
     > li {
       display: inline-block;
+      margin-top: $gl-padding-4;
+      margin-bottom: $gl-padding-4;
 
       &:not(:last-child) {
         margin-right: $gl-padding;
@@ -711,36 +716,32 @@ a.deploy-project-label {
           float: right;
         }
       }
+    }
 
-      > a {
-        padding: 0;
-        background-color: transparent;
-        font-size: 14px;
-        line-height: 29px;
-        color: $notes-light-color;
+    .stat-text,
+    .stat-link {
+      padding: $gl-btn-vert-padding 0;
+      background-color: transparent;
+      font-size: $gl-font-size;
+      line-height: $gl-btn-line-height;
+      color: $notes-light-color;
+    }
 
-        &:hover,
-        &:focus {
-          color: $gl-text-color;
-          text-decoration: underline;
-        }
+    .stat-link {
+      &:hover,
+      &:focus {
+        color: $gl-text-color;
+        text-decoration: underline;
       }
     }
-  }
-
-  li.missing {
-    border: 1px dashed $border-gray-normal-dashed;
-    border-radius: $border-radius-default;
 
-    a {
-      padding-left: 10px;
-      padding-right: 10px;
-      color: $notes-light-color;
-      display: block;
+    .btn {
+      padding: $gl-btn-vert-padding $gl-btn-horz-padding;
+      line-height: $gl-btn-line-height;
     }
 
-    &:hover {
-      background-color: $gray-normal;
+    .btn-missing {
+      @extend .btn-missing;
     }
   }
 }
@@ -750,7 +751,7 @@ pre.light-well {
 }
 
 .git-empty {
-  margin: 0 7px 7px;
+  margin-bottom: 7px;
 
   h5 {
     color: $gl-text-color;
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 8460d1b51d9..3bb9ff38794 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -637,4 +637,218 @@ module ProjectsHelper
 
     project_find_file_path(@project, ref)
   end
+
+  def can_current_user_push_code?(project)
+    project.empty_repo? ? can?(current_user, :push_code, project) : can_push_branch?(project, project.default_branch)
+  end
+
+  def files_anchor_data(project)
+    {
+      enabled: true,
+      label: _('Files (%{human_size})') % { human_size: storage_counter(@project.statistics.total_repository_size) },
+      link: project_tree_path(@project)
+    }
+  end
+
+  def commits_anchor_data(project)
+    {
+      enabled: true,
+      label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', @project.statistics.commit_count) % { commit_count: number_with_delimiter(@project.statistics.commit_count) },
+      link: project_commits_path(@project, current_ref)
+    }
+  end
+
+  def branches_anchor_data(project)
+    {
+      enabled: true,
+      label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', @repository.branch_count) % { branch_count: number_with_delimiter(@repository.branch_count) },
+      link: project_branches_path(@project)
+    }
+  end
+
+  def tags_anchor_data(project)
+    {
+      enabled: true,
+      label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', @repository.tag_count) % { tag_count: number_with_delimiter(@repository.tag_count) },
+      link: project_tags_path(@project)
+    }
+  end
+
+  def new_file_anchor_data(project)
+    if current_user && can_current_user_push_code?(project)
+      {
+        enabled: false,
+        label: _('New file'),
+        link: project_new_blob_path(project, project.default_branch || 'master'),
+        class_modifier: 'new'
+      }
+    end
+  end
+
+  def readme_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && project.repository.readme.blank?
+      {
+        enabled: false,
+        label: _('Add Readme'),
+        link: add_special_file_path(project, file_name: 'README.md')
+      }
+    elsif project.repository.readme.present?
+      {
+        enabled: true,
+        label: _('Readme'),
+        link: default_project_view != 'readme' ? readme_path(@project) : '#readme'
+      }
+    end
+  end
+
+  def changelog_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && project.repository.changelog.blank?
+      {
+        enabled: false,
+        label: _('Add Changelog'),
+        link: add_special_file_path(project, file_name: 'CHANGELOG')
+      }
+    elsif project.repository.changelog.present?
+      {
+        enabled: true,
+        label: _('Changelog'),
+        link: changelog_path(project)
+      }
+    end
+  end
+
+  def license_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && project.repository.license_blob.blank?
+      {
+        enabled: false,
+        label: _('Add License'),
+        link: add_special_file_path(project, file_name: 'LICENSE')
+      }
+    elsif project.repository.license_blob.present?
+      {
+        enabled: true,
+        label: license_short_name(project),
+        link: license_path(project)
+      }
+    end
+  end
+
+  def contribution_guide_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && project.repository.contribution_guide.blank?
+      {
+        enabled: false,
+        label: _('Add Contribution guide'),
+        link: add_special_file_path(project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide')
+      }
+    elsif project.repository.contribution_guide.present?
+      {
+        enabled: true,
+        label: _('Contribution guide'),
+        link: contribution_guide_path(@project)
+      }
+    end
+  end
+
+  def autodevops_anchor_data(project, ignore_callout: false)
+    if current_user && can?(current_user, :admin_pipeline, project) && project.repository.gitlab_ci_yml.blank? && (ignore_callout || !show_auto_devops_callout?(project))
+      {
+        enabled: project.auto_devops_enabled?,
+        label: project.auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
+        link: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')
+      }
+    elsif project.auto_devops_enabled?
+      {
+        enabled: true,
+        label: _('Auto DevOps enabled'),
+        link: nil
+      }
+    end
+  end
+
+  def kubernetes_cluster_anchor_data(project)
+    if current_user && can?(current_user, :create_cluster, project)
+      cluster_link = project.clusters.size == 1 ? project_cluster_path(project, project.clusters.first) : project_clusters_path(project)
+
+      if project.clusters.empty?
+        cluster_link = new_project_cluster_path(project)
+      end
+
+      {
+        enabled: !project.clusters.empty?,
+        label: project.clusters.empty? ? _('Add Kubernetes cluster') : n_('Kubernetes cluster', 'Kubernetes clusters', project.clusters.size),
+        link: cluster_link
+      }
+    end
+  end
+
+  def gitlab_ci_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && project.repository.gitlab_ci_yml.blank? && !project.auto_devops_enabled?
+      {
+        enabled: false,
+        label: _('Set up CI/CD'),
+        link: add_special_file_path(project, file_name: '.gitlab-ci.yml')
+      }
+    elsif project.repository.gitlab_ci_yml.present?
+      {
+        enabled: true,
+        label: _('CI/CD configuration'),
+        link: ci_configuration_path(@project)
+      }
+    end
+  end
+
+  def koding_anchor_data(project)
+    if current_user && can_current_user_push_code?(project) && koding_enabled? && project.repository.koding_yml.blank?
+      {
+        enabled: false,
+        label: _('Set up Koding'),
+        link: add_koding_stack_path(project)
+      }
+    end
+  end
+
+  def empty_project_stat_anchor_items(project)
+    [
+      autodevops_anchor_data(project, ignore_callout: true),
+      kubernetes_cluster_anchor_data(project)
+    ].compact.reject { |i| !i[:enabled] }
+  end
+
+  def empty_project_stat_button_items(project)
+    [
+      new_file_anchor_data(project),
+      readme_anchor_data(project),
+      license_anchor_data(project),
+      autodevops_anchor_data(project, ignore_callout: true),
+      kubernetes_cluster_anchor_data(project)
+    ].compact.reject { |i| i[:enabled] }
+  end
+
+  def project_stat_anchor_items(project)
+    [
+      files_anchor_data(project),
+      commits_anchor_data(project),
+      branches_anchor_data(project),
+      tags_anchor_data(project),
+      readme_anchor_data(project),
+      changelog_anchor_data(project),
+      license_anchor_data(project),
+      contribution_guide_anchor_data(project),
+      gitlab_ci_anchor_data(project),
+      autodevops_anchor_data(project),
+      kubernetes_cluster_anchor_data(project)
+    ].compact.reject { |i| !i[:enabled] }
+  end
+
+  def project_stat_button_items(project)
+    [
+      changelog_anchor_data(project),
+      license_anchor_data(project),
+      contribution_guide_anchor_data(project),
+      autodevops_anchor_data(project),
+      kubernetes_cluster_anchor_data(project),
+      gitlab_ci_anchor_data(project),
+      koding_anchor_data(project)
+    ].compact.reject { |i| i[:enabled] }
+  end
 end
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
new file mode 100644
index 00000000000..9ca51f8d68c
--- /dev/null
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -0,0 +1,5 @@
+- anchors = local_assigns.fetch(:anchors, [])
+
+- if anchors.size > 0
+  %ul.nav
+    = render partial: 'stat_anchor_list_item', collection: anchors, as: :anchor
diff --git a/app/views/projects/_stat_anchor_list_item.html.haml b/app/views/projects/_stat_anchor_list_item.html.haml
new file mode 100644
index 00000000000..404dd24599f
--- /dev/null
+++ b/app/views/projects/_stat_anchor_list_item.html.haml
@@ -0,0 +1,7 @@
+%li
+  - if anchor[:link]
+    = link_to anchor[:link], class: anchor[:enabled] ? 'stat-link' : "btn btn-#{anchor[:class_modifier] || 'missing'}" do
+      = anchor[:label]
+  - else
+    %span.stat-text
+      = anchor[:label]
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index ab225796b12..01fb9dab313 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -5,38 +5,41 @@
 
 = render "home_panel"
 
-.row-content-block.second-block.center
-  %h4
-    The repository for this project is empty
+.project-empty-note-panel
+  %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
+    .prepend-top-20
+    %h4
+      = _('The repository for this project is empty')
+
+    - if can?(current_user, :push_code, @project)
+      %p
+        - link_to_cli = link_to _('command line instructions'), '#repo-command-line-instructions'
+        = _('If you already have files you can push them using the %{link_to_cli} below.').html_safe % { link_to_cli: link_to_cli }
+      %p
+        %em
+          - link_to_protected_branches = link_to _('Learn more about protected branches'), help_page_path('user/project/protected_branches')
+          = _('Note that the master branch is automatically protected. %{link_to_protected_branches}').html_safe % { link_to_protected_branches: link_to_protected_branches }
 
-  - if can?(current_user, :push_code, @project)
-    %p
-      If you already have files you can push them using command line instructions below.
-    %p
-      Otherwise you can start with adding a
-      = succeed ',' do
-        = link_to "README", add_special_file_path(@project, file_name: 'README.md')
-      a
-      = succeed ',' do
-        = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
-      or a
-      = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
-      to this project.
-    %p
-      You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
+      %hr
+      %p
+        - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
+        - link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), project_clusters_path(@project))
+        = s_('AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
 
-    - if show_auto_devops_callout?(@project)
+      %hr
       %p
-        - link = link_to(s_('AutoDevOps|Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
-        = s_('AutoDevOps|You can activate %{link_to_settings} for this project.').html_safe % { link_to_settings: link }
-      %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
-      %p= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master'), class: 'btn btn-new'
+        = _('Otherwise it is recommended you start with one of the options below.')
+      .prepend-top-20
+
+%nav.project-stats{ class: container_class }
+  = render 'stat_anchor_list', anchors: empty_project_stat_anchor_items(@project)
+  = render 'stat_anchor_list', anchors: empty_project_stat_button_items(@project)
 
 - if can?(current_user, :push_code, @project)
-  %div{ class: container_class }
+  %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
     .prepend-top-20
     .empty_wrapper
-      %h3.page-title-empty
+      %h3#repo-command-line-instructions.page-title-empty
         Command line instructions
       .git-empty
         %fieldset
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 888d820b04e..3d23b19b815 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -14,65 +14,9 @@
 
 - if can?(current_user, :download_code, @project)
   %nav.project-stats{ class: container_class }
-    %ul.nav
-      %li
-        = link_to project_tree_path(@project) do
-          #{_('Files')} (#{storage_counter(@project.statistics.total_repository_size)})
-      %li
-        = link_to project_commits_path(@project, current_ref) do
-          #{n_('Commit', 'Commits', @project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
-      %li
-        = link_to project_branches_path(@project) do
-          #{n_('Branch', 'Branches', @repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
-      %li
-        = link_to project_tags_path(@project) do
-          #{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
+    = render 'stat_anchor_list', anchors: project_stat_anchor_items(@project)
+    = render 'stat_anchor_list', anchors: project_stat_button_items(@project)
 
-      - if @repository.readme
-        %li
-          = link_to _('Readme'),
-            default_project_view != 'readme' ? readme_path(@project) : '#readme'
-
-      - if @repository.changelog
-        %li
-          = link_to _('Changelog'), changelog_path(@project)
-
-      - if @repository.license_blob
-        %li
-          = link_to license_short_name(@project), license_path(@project)
-
-      - if @repository.contribution_guide
-        %li
-          = link_to _('Contribution guide'), contribution_guide_path(@project)
-
-      - if @repository.gitlab_ci_yml
-        %li
-          = link_to _('CI/CD configuration'), ci_configuration_path(@project)
-
-      - if current_user && can_push_branch?(@project, @project.default_branch)
-        - unless @repository.changelog
-          %li.missing
-            = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
-              #{ _('Add Changelog') }
-        - unless @repository.license_blob
-          %li.missing
-            = link_to add_special_file_path(@project, file_name: 'LICENSE') do
-              #{ _('Add License') }
-        - unless @repository.contribution_guide
-          %li.missing
-            = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
-              #{ _('Add Contribution guide') }
-        - unless @repository.gitlab_ci_yml
-          %li.missing
-            = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
-              #{ _('Set up CI/CD') }
-        - if koding_enabled? && @repository.koding_yml.blank?
-          %li.missing
-            = link_to  _('Set up Koding'), add_koding_stack_path(@project)
-        - if @repository.gitlab_ci_yml.blank? && @project.deployment_platform.present?
-          %li.missing
-            = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
-              #{ _('Set up auto deploy') }
 
 %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
   - if @project.archived?
diff --git a/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml b/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml
new file mode 100644
index 00000000000..5613b2af763
--- /dev/null
+++ b/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml
@@ -0,0 +1,6 @@
+---
+title: Add a button on the project page to set up a Kubernetes cluster and enable
+  Auto DevOps
+merge_request: 16900
+author:
+type: added
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
deleted file mode 100644
index 9aef68b7156..00000000000
--- a/spec/features/auto_deploy_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'spec_helper'
-
-describe 'Auto deploy' do
-  let(:user) { create(:user) }
-  let(:project) { create(:project, :repository) }
-
-  shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
-    context 'when no deployment service is active' do
-      before do
-        trun_off
-      end
-
-      it 'does not show a button to set up auto deploy' do
-        visit project_path(project)
-        expect(page).to have_no_content('Set up auto deploy')
-      end
-    end
-
-    context 'when a deployment service is active' do
-      before do
-        trun_on
-        visit project_path(project)
-      end
-
-      it 'shows a button to set up auto deploy' do
-        expect(page).to have_link('Set up auto deploy')
-      end
-
-      it 'includes OpenShift as an available template', :js do
-        click_link 'Set up auto deploy'
-        click_button 'Apply a GitLab CI Yaml template'
-
-        within '.gitlab-ci-yml-selector' do
-          expect(page).to have_content('OpenShift')
-        end
-      end
-
-      it 'creates a merge request using "auto-deploy" branch', :js do
-        click_link 'Set up auto deploy'
-        click_button 'Apply a GitLab CI Yaml template'
-        within '.gitlab-ci-yml-selector' do
-          click_on 'OpenShift'
-        end
-        wait_for_requests
-        click_button 'Commit changes'
-
-        expect(page).to have_content('New Merge Request From auto-deploy into master')
-      end
-    end
-  end
-
-  context 'when user configured kubernetes from Integration > Kubernetes' do
-    before do
-      create :kubernetes_service, project: project
-      project.add_master(user)
-      sign_in user
-    end
-
-    let(:trun_on) { project.deployment_platform.update!(active: true) }
-    let(:trun_off) { project.deployment_platform.update!(active: false) }
-
-    it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
-  end
-
-  context 'when user configured kubernetes from CI/CD > Clusters' do
-    before do
-      create(:cluster, :provided_by_gcp, projects: [project])
-      project.add_master(user)
-      sign_in user
-    end
-
-    let(:trun_on) { project.deployment_platform.cluster.update!(enabled: true) }
-    let(:trun_off) { project.deployment_platform.cluster.update!(enabled: false) }
-
-    it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
-  end
-end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 8ac9821b879..7f1d1934103 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -11,7 +11,7 @@ feature 'project owner sees a link to create a license file in empty project', :
 
   scenario 'project master creates a license file from a template' do
     visit project_path(project)
-    click_on 'LICENSE'
+    click_on 'Add License'
     expect(page).to have_content('New file')
 
     expect(current_path).to eq(
diff --git a/spec/features/projects/show_project_spec.rb b/spec/features/projects/show_project_spec.rb
index 0b94c9eae5d..f5f2bbb49e0 100644
--- a/spec/features/projects/show_project_spec.rb
+++ b/spec/features/projects/show_project_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe 'Project show page', :feature do
+  include ProjectsHelper
+
   context 'when project pending delete' do
     let(:project) { create(:project, :empty_repo, pending_delete: true) }
 
@@ -17,4 +19,319 @@ describe 'Project show page', :feature do
       expect(page).to have_content("This project was scheduled for deletion, but failed with the following message: #{project.delete_error}")
     end
   end
+
+  describe 'stat button existence' do
+    # For "New file", "Add License" functionality,
+    # see spec/features/projects/files/project_owner_creates_license_file_spec.rb
+    # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+
+    let(:user) { create(:user) }
+
+    describe 'empty project' do
+      let(:project) { create(:project, :public, :empty_repo) }
+
+      describe 'as a normal user' do
+        before do
+          sign_in(user)
+
+          visit project_path(project)
+        end
+
+        it 'no Auto DevOps button if can not manage pipelines' do
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Enable Auto DevOps')
+            expect(page).not_to have_link('Auto DevOps enabled')
+          end
+        end
+
+        it '"Auto DevOps enabled" button not linked' do
+          project.create_auto_devops!(enabled: true)
+
+          visit project_path(project)
+
+          page.within('.project-stats') do
+            expect(page).to have_text('Auto DevOps enabled')
+          end
+        end
+      end
+
+      describe 'as a master' do
+        before do
+          project.add_master(user)
+          sign_in(user)
+
+          visit project_path(project)
+        end
+
+        it '"New file" button linked to new file page' do
+          page.within('.project-stats') do
+            expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master'))
+          end
+        end
+
+        it '"Add Readme" button linked to new file populated for a readme' do
+          page.within('.project-stats') do
+            expect(page).to have_link('Add Readme', href: add_special_file_path(project, file_name: 'README.md'))
+          end
+        end
+
+        it '"Add License" button linked to new file populated for a license' do
+          page.within('.project-stats') do
+            expect(page).to have_link('Add License', href: add_special_file_path(project, file_name: 'LICENSE'))
+          end
+        end
+
+        describe 'Auto DevOps button' do
+          it '"Enable Auto DevOps" button linked to settings page' do
+            page.within('.project-stats') do
+              expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+            end
+          end
+
+          it '"Auto DevOps enabled" anchor linked to settings page' do
+            project.create_auto_devops!(enabled: true)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+            end
+          end
+        end
+
+        describe 'Kubernetes cluster button' do
+          it '"Add Kubernetes cluster" button linked to clusters page' do
+            page.within('.project-stats') do
+              expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
+            end
+          end
+
+          it '"Kubernetes cluster" anchor linked to cluster page' do
+            cluster = create(:cluster, :provided_by_gcp, projects: [project])
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Kubernetes cluster', href: project_cluster_path(project, cluster))
+            end
+          end
+        end
+      end
+    end
+
+    describe 'populated project' do
+      let(:project) { create(:project, :public, :repository) }
+
+      describe 'as a normal user' do
+        before do
+          sign_in(user)
+
+          visit project_path(project)
+        end
+
+        it 'no Auto DevOps button if can not manage pipelines' do
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Enable Auto DevOps')
+            expect(page).not_to have_link('Auto DevOps enabled')
+          end
+        end
+
+        it '"Auto DevOps enabled" button not linked' do
+          project.create_auto_devops!(enabled: true)
+
+          visit project_path(project)
+
+          page.within('.project-stats') do
+            expect(page).to have_text('Auto DevOps enabled')
+          end
+        end
+
+        it 'no Kubernetes cluster button if can not manage clusters' do
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Add Kubernetes cluster')
+            expect(page).not_to have_link('Kubernetes cluster')
+          end
+        end
+      end
+
+      describe 'as a master' do
+        before do
+          allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
+          project.add_master(user)
+          sign_in(user)
+
+          visit project_path(project)
+        end
+
+        it 'no "Add Changelog" button if the project already has a changelog' do
+          expect(project.repository.changelog).not_to be_nil
+
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Add Changelog')
+          end
+        end
+
+        it 'no "Add License" button if the project already has a license' do
+          expect(project.repository.license_blob).not_to be_nil
+
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Add License')
+          end
+        end
+
+        it 'no "Add Contribution guide" button if the project already has a contribution guide' do
+          expect(project.repository.contribution_guide).not_to be_nil
+
+          page.within('.project-stats') do
+            expect(page).not_to have_link('Add Contribution guide')
+          end
+        end
+
+        describe 'GitLab CI configuration button' do
+          it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
+            expect(project.repository.gitlab_ci_yml).to be_nil
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Set up CI/CD', href: add_special_file_path(project, file_name: '.gitlab-ci.yml'))
+            end
+          end
+
+          it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
+            Files::CreateService.new(
+              project,
+              project.creator,
+              start_branch: 'master',
+              branch_name: 'master',
+              commit_message: "Add .gitlab-ci.yml",
+              file_path: '.gitlab-ci.yml',
+              file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+            ).execute
+
+            expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Set up CI/CD')
+            end
+          end
+
+          it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
+            project.create_auto_devops!(enabled: true)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Set up CI/CD')
+            end
+          end
+        end
+
+        describe 'Auto DevOps button' do
+          it '"Enable Auto DevOps" button linked to settings page' do
+            page.within('.project-stats') do
+              expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+            end
+          end
+
+          it '"Enable Auto DevOps" button linked to settings page' do
+            project.create_auto_devops!(enabled: true)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+            end
+          end
+
+          it 'no Auto DevOps button if Auto DevOps callout is shown' do
+            allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(true)
+
+            visit project_path(project)
+
+            expect(page).to have_selector('.js-autodevops-banner')
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Enable Auto DevOps')
+              expect(page).not_to have_link('Auto DevOps enabled')
+            end
+          end
+
+          it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
+            Files::CreateService.new(
+              project,
+              project.creator,
+              start_branch: 'master',
+              branch_name: 'master',
+              commit_message: "Add .gitlab-ci.yml",
+              file_path: '.gitlab-ci.yml',
+              file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+            ).execute
+
+            expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Enable Auto DevOps')
+              expect(page).not_to have_link('Auto DevOps enabled')
+            end
+          end
+        end
+
+        describe 'Kubernetes cluster button' do
+          it '"Add Kubernetes cluster" button linked to clusters page' do
+            page.within('.project-stats') do
+              expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
+            end
+          end
+
+          it '"Kubernetes cluster" button linked to cluster page' do
+            cluster = create(:cluster, :provided_by_gcp, projects: [project])
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Kubernetes cluster', href: project_cluster_path(project, cluster))
+            end
+          end
+        end
+
+        describe '"Set up Koding" button' do
+          it 'no "Set up Koding" button if Koding disabled' do
+            stub_application_setting(koding_enabled?: false)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Set up Koding')
+            end
+          end
+
+          it 'no "Set up Koding" button if the project already has a .koding.yml' do
+            stub_application_setting(koding_enabled?: true)
+            allow(Gitlab::CurrentSettings.current_application_settings).to receive(:koding_url).and_return('http://koding.example.com')
+            expect(project.repository.changelog).not_to be_nil
+            allow_any_instance_of(Repository).to receive(:koding_yml).and_return(project.repository.changelog)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).not_to have_link('Set up Koding')
+            end
+          end
+
+          it '"Set up Koding" button linked to new file populated for a .koding.yml' do
+            stub_application_setting(koding_enabled?: true)
+
+            visit project_path(project)
+
+            page.within('.project-stats') do
+              expect(page).to have_link('Set up Koding', href: add_koding_stack_path(project))
+            end
+          end
+        end
+      end
+    end
+  end
 end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 4662367d843..b625e7065cc 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -13,7 +13,7 @@ feature 'Master views tags' do
 
     before do
       visit project_path(project)
-      click_on 'README'
+      click_on 'Add Readme'
       fill_in :commit_message, with: 'Add a README file', visible: true
       click_button 'Commit changes'
       visit project_tags_path(project)
-- 
2.30.9