diff options
author | Valery Sizov <valery@gitlab.com> | 2016-12-14 21:19:43 +0300 |
---|---|---|
committer | Valery Sizov <valery@gitlab.com> | 2016-12-14 21:19:43 +0300 |
commit | 076cdd204459496d7cdd14b86838c2c2c7875cd4 (patch) | |
tree | 055cd9ab371205963df595f87ec201e75d4877f3 | |
parent | c756e62b08f0a639f5550d17339b2938c9c9e096 (diff) | |
parent | 6910bf29b6a93aaf65ae5fa3261da6df2d03ec44 (diff) |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into bitbucket-oauth2
85 files changed, 1346 insertions, 211 deletions
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index 57146e1fccd..5aef31724e1 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -232,6 +232,7 @@ &:hover:not(.active) { background-color: $gray-lightest; box-shadow: inset 2px 0 0 0 $border-color; + cursor: pointer; } &:first-child { diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e19752a8a1..d9f5e01f0dc 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -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 diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 88c46076df6..e7cf606a7ae 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -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? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fda8228a1e9..54f73171fd4 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -336,8 +336,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 diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index d2a37c0a827..7ef59445d77 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -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 diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index cf90475f4d4..31cd381dcd2 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -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 diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 37374044551..f0479d94986 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -17,14 +17,13 @@ class Namespace < ActiveRecord::Base validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, presence: true, - uniqueness: true, + uniqueness: { scope: :parent_id }, length: { maximum: 255 }, namespace_name: true validates :description, length: { maximum: 255 } validates :path, presence: true, - uniqueness: { case_sensitive: false }, length: { maximum: 255 }, namespace: true diff --git a/app/views/admin/dashboard/_head.html.haml b/app/views/admin/dashboard/_head.html.haml index ec40391a3e3..b5f96363230 100644 --- a/app/views/admin/dashboard/_head.html.haml +++ b/app/views/admin/dashboard/_head.html.haml @@ -8,7 +8,7 @@ %span Overview = nav_link(controller: [:admin, :projects]) do - = link_to admin_namespaces_projects_path, title: 'Projects' do + = link_to admin_projects_path, title: 'Projects' do %span Projects = nav_link(controller: :users) do diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index e51f4ac1d93..5238623e936 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -116,7 +116,7 @@ .light-well.well-centered %h4 Projects .data - = link_to admin_namespaces_projects_path do + = link_to admin_projects_path do %h1= number_with_delimiter(Project.cached_count) %hr = link_to('New Project', new_project_path, class: "btn btn-new") diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index 664bb417c6a..4efeec0ea4e 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -2,7 +2,7 @@ %li.group-row{ class: css_class } .controls - = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn' + = link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn' = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove' .stats %span diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 40871e32913..71a605f33b1 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -2,7 +2,7 @@ %h3.page-title Group: #{@group.name} - = link_to edit_admin_group_path(@group), class: "btn pull-right" do + = link_to admin_group_edit_path(@group), class: "btn pull-right" do %i.fa.fa-pencil-square-o Edit %hr @@ -88,7 +88,7 @@ Read more about project permissions %strong= link_to "here", help_page_path("user/permissions"), class: "vlink" - = form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do + = form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do %div = users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all) %div.prepend-top-10 diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index b37b8d4fee7..8bc7dc7dd51 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -7,7 +7,7 @@ %div{ class: container_class } .top-area .prepend-top-default - = form_tag admin_namespaces_projects_path, method: :get do |f| + = form_tag admin_projects_path, method: :get do |f| .search-holder .search-field-holder = search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name' @@ -41,19 +41,19 @@ = button_tag "Search", class: "btn btn-primary btn-search" %ul.nav-links - - opts = params[:visibility_level].present? ? {} : { page: admin_namespaces_projects_path } + - opts = params[:visibility_level].present? ? {} : { page: admin_projects_path } = nav_link(opts) do - = link_to admin_namespaces_projects_path do + = link_to admin_projects_path do All = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s ? 'active' : '' }) do - = link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do + = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do Private = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s ? 'active' : '' }) do - = link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do + = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do Internal = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s ? 'active' : '' }) do - = link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do + = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do Public .nav-controls diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 73038164056..ca503e35623 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -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 diff --git a/app/views/ci/status/_badge.html.haml b/app/views/ci/status/_badge.html.haml new file mode 100644 index 00000000000..f2135af2686 --- /dev/null +++ b/app/views/ci/status/_badge.html.haml @@ -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 diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml index f6aa20c4579..057a720a54a 100644 --- a/app/views/projects/builds/_header.html.haml +++ b/app/views/projects/builds/_header.html.haml @@ -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 diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 18b3b04154f..f1cb0201032 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -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) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index b58dceb58c9..3f05a21990f 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -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 diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 7f751d9ae2e..9f444f076c0 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -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 diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 229bdfb0e8d..b00ba2d5307 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -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) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index 1d8fa10db0c..bf8c75b6e5c 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -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 diff --git a/app/views/repository_check_mailer/notify.html.haml b/app/views/repository_check_mailer/notify.html.haml index a585147ddd1..94e5a5d9709 100644 --- a/app/views/repository_check_mailer/notify.html.haml +++ b/app/views/repository_check_mailer/notify.html.haml @@ -2,7 +2,7 @@ #{@message}. %p - = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1) + = link_to "See the affected projects in the GitLab admin panel", admin_projects_url(last_repository_check_failed: 1) %p You are receiving this message because you are a GitLab administrator for #{Gitlab.config.gitlab.url}. diff --git a/app/views/repository_check_mailer/notify.text.haml b/app/views/repository_check_mailer/notify.text.haml index 93db151329e..0902c50d052 100644 --- a/app/views/repository_check_mailer/notify.text.haml +++ b/app/views/repository_check_mailer/notify.text.haml @@ -1,6 +1,6 @@ #{@message}. \ -View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)} +View details: #{admin_projects_url(last_repository_check_failed: 1)} You are receiving this message because you are a GitLab administrator for #{Gitlab.config.gitlab.url}. diff --git a/app/views/shared/icons/_icon_status_manual.svg b/app/views/shared/icons/_icon_status_manual.svg new file mode 100755 index 00000000000..c98839f51a9 --- /dev/null +++ b/app/views/shared/icons/_icon_status_manual.svg @@ -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> diff --git a/changelogs/unreleased/24803-change-cursor-for-ca-stages.yml b/changelogs/unreleased/24803-change-cursor-for-ca-stages.yml new file mode 100644 index 00000000000..b9d84c0ce31 --- /dev/null +++ b/changelogs/unreleased/24803-change-cursor-for-ca-stages.yml @@ -0,0 +1,5 @@ +--- +title: Changed cursor icon to pointer when mousing over stages on the Cycle Analytics + pages +merge_request: +author: Ryan Harris diff --git a/changelogs/unreleased/24927-custom-event-polyfill-test.yml b/changelogs/unreleased/24927-custom-event-polyfill-test.yml new file mode 100644 index 00000000000..879c28a951e --- /dev/null +++ b/changelogs/unreleased/24927-custom-event-polyfill-test.yml @@ -0,0 +1,4 @@ +--- +title: Adds tests for custom event polyfill +merge_request: 7996 +author: diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 5ae985da561..0dd2c8f7aef 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -28,9 +28,19 @@ namespace :admin do resources :applications - resources :groups, constraints: { id: /[^\/]+/ } do - member do + resources :groups, only: [:index, :new, :create] + + scope(path: 'groups/*id', + controller: :groups, + constraints: { id: Gitlab::Regex.namespace_route_regex }) do + + scope(as: :group) do put :members_update + get :edit, action: :edit + get '/', action: :show + patch '/', action: :update + put '/', action: :update + delete '/', action: :destroy end end @@ -50,14 +60,13 @@ namespace :admin do resource :system_info, controller: 'system_info', only: [:show] resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.html/ } - resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do - root to: 'projects#index', as: :projects + resources :projects, only: [:index] + scope(path: 'projects/*namespace_id', as: :namespace) do resources(:projects, path: '/', - constraints: { id: /[a-zA-Z.0-9_\-]+/ }, - only: [:index, :show]) do - root to: 'projects#show' + constraints: { id: Gitlab::Regex.project_route_regex }, + only: [:show]) do member do put :transfer diff --git a/db/migrate/20161206153749_remove_uniq_path_index_from_namespace.rb b/db/migrate/20161206153749_remove_uniq_path_index_from_namespace.rb new file mode 100644 index 00000000000..2977917f2d1 --- /dev/null +++ b/db/migrate/20161206153749_remove_uniq_path_index_from_namespace.rb @@ -0,0 +1,36 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveUniqPathIndexFromNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + DOWNTIME = false + + def up + constraint_name = 'namespaces_path_key' + + transaction do + if index_exists?(:namespaces, :path) + remove_index(:namespaces, :path) + end + + # In some bizarre cases PostgreSQL might have a separate unique constraint + # that we'll need to drop. + if constraint_exists?(constraint_name) && Gitlab::Database.postgresql? + execute("ALTER TABLE namespaces DROP CONSTRAINT IF EXISTS #{constraint_name};") + end + end + end + + def down + unless index_exists?(:namespaces, :path) + add_concurrent_index(:namespaces, :path, unique: true) + end + end + + def constraint_exists?(name) + indexes(:namespaces).map(&:name).include?(name) + end +end diff --git a/db/migrate/20161206153751_add_path_index_to_namespace.rb b/db/migrate/20161206153751_add_path_index_to_namespace.rb new file mode 100644 index 00000000000..b0bac7d121e --- /dev/null +++ b/db/migrate/20161206153751_add_path_index_to_namespace.rb @@ -0,0 +1,20 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddPathIndexToNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + DOWNTIME = false + + def up + add_concurrent_index :namespaces, :path + end + + def down + if index_exists?(:namespaces, :path) + remove_index :namespaces, :path + end + end +end diff --git a/db/migrate/20161206153753_remove_uniq_name_index_from_namespace.rb b/db/migrate/20161206153753_remove_uniq_name_index_from_namespace.rb new file mode 100644 index 00000000000..cc9d4974baa --- /dev/null +++ b/db/migrate/20161206153753_remove_uniq_name_index_from_namespace.rb @@ -0,0 +1,36 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveUniqNameIndexFromNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + DOWNTIME = false + + def up + constraint_name = 'namespaces_name_key' + + transaction do + if index_exists?(:namespaces, :name) + remove_index(:namespaces, :name) + end + + # In some bizarre cases PostgreSQL might have a separate unique constraint + # that we'll need to drop. + if constraint_exists?(constraint_name) && Gitlab::Database.postgresql? + execute("ALTER TABLE namespaces DROP CONSTRAINT IF EXISTS #{constraint_name};") + end + end + end + + def down + unless index_exists?(:namespaces, :name) + add_concurrent_index(:namespaces, :name, unique: true) + end + end + + def constraint_exists?(name) + indexes(:namespaces).map(&:name).include?(name) + end +end diff --git a/db/migrate/20161206153754_add_name_index_to_namespace.rb b/db/migrate/20161206153754_add_name_index_to_namespace.rb new file mode 100644 index 00000000000..aaa35ed6f0a --- /dev/null +++ b/db/migrate/20161206153754_add_name_index_to_namespace.rb @@ -0,0 +1,20 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddNameIndexToNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + DOWNTIME = false + + def up + add_concurrent_index(:namespaces, [:name, :parent_id], unique: true) + end + + def down + if index_exists?(:namespaces, :name) + remove_index :namespaces, [:name, :parent_id] + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ae47456084f..4711b7873af 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -744,11 +744,11 @@ ActiveRecord::Schema.define(version: 20161212142807) do add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["deleted_at"], name: "index_namespaces_on_deleted_at", using: :btree - add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree + add_index "namespaces", ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["parent_id", "id"], name: "index_namespaces_on_parent_id_and_id", unique: true, using: :btree - add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree + add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree @@ -1290,4 +1290,4 @@ ActiveRecord::Schema.define(version: 20161212142807) do add_foreign_key "subscriptions", "projects", on_delete: :cascade add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "u2f_registrations", "users" -end +end
\ No newline at end of file diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md index a0e895773ce..c64d3407461 100644 --- a/doc/update/8.13-to-8.14.md +++ b/doc/update/8.13-to-8.14.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-14-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v4.0.0 +sudo -u git -H git checkout v4.0.3 ``` ### 6. Update gitlab-workhorse diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index d36eff5cf16..82c07d4f536 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -168,7 +168,7 @@ module SharedPaths end step 'I visit admin projects page' do - visit admin_namespaces_projects_path + visit admin_projects_path end step 'I visit admin users page' do diff --git a/lib/gitlab/allowable.rb b/lib/gitlab/allowable.rb new file mode 100644 index 00000000000..f48abcc86d5 --- /dev/null +++ b/lib/gitlab/allowable.rb @@ -0,0 +1,7 @@ +module Gitlab + module Allowable + def can?(user, action, subject) + Ability.allowed?(user, action, subject) + end + end +end diff --git a/lib/gitlab/ci/status/build/cancelable.rb b/lib/gitlab/ci/status/build/cancelable.rb new file mode 100644 index 00000000000..a979fe7d573 --- /dev/null +++ b/lib/gitlab/ci/status/build/cancelable.rb @@ -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 diff --git a/lib/gitlab/ci/status/build/common.rb b/lib/gitlab/ci/status/build/common.rb new file mode 100644 index 00000000000..3fec2c5d4db --- /dev/null +++ b/lib/gitlab/ci/status/build/common.rb @@ -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 diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb new file mode 100644 index 00000000000..eee9a64120b --- /dev/null +++ b/lib/gitlab/ci/status/build/factory.rb @@ -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 diff --git a/lib/gitlab/ci/status/build/play.rb b/lib/gitlab/ci/status/build/play.rb new file mode 100644 index 00000000000..5c506e6d59f --- /dev/null +++ b/lib/gitlab/ci/status/build/play.rb @@ -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 diff --git a/lib/gitlab/ci/status/build/retryable.rb b/lib/gitlab/ci/status/build/retryable.rb new file mode 100644 index 00000000000..8e38d6a8523 --- /dev/null +++ b/lib/gitlab/ci/status/build/retryable.rb @@ -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 diff --git a/lib/gitlab/ci/status/build/stop.rb b/lib/gitlab/ci/status/build/stop.rb new file mode 100644 index 00000000000..f8ffa95cde4 --- /dev/null +++ b/lib/gitlab/ci/status/build/stop.rb @@ -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 diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index ce4108fdcf2..46fef8262c1 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -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 diff --git a/lib/gitlab/ci/status/extended.rb b/lib/gitlab/ci/status/extended.rb index 6bfb5d38c1f..d367c9bda69 100644 --- a/lib/gitlab/ci/status/extended.rb +++ b/lib/gitlab/ci/status/extended.rb @@ -2,8 +2,12 @@ module Gitlab module Ci module Status module Extended - def matches?(_subject) - raise NotImplementedError + extend ActiveSupport::Concern + + class_methods do + def matches?(_subject, _user) + raise NotImplementedError + end end end end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb index b2f896f2211..ae9ef895df4 100644 --- a/lib/gitlab/ci/status/factory.rb +++ b/lib/gitlab/ci/status/factory.rb @@ -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 diff --git a/lib/gitlab/ci/status/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb index 25e52bec3da..76bfd18bf40 100644 --- a/lib/gitlab/ci/status/pipeline/common.rb +++ b/lib/gitlab/ci/status/pipeline/common.rb @@ -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? diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb index 4ac4ec671d0..16dcb326be9 100644 --- a/lib/gitlab/ci/status/pipeline/factory.rb +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -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 diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb index 4b040d60df8..a7c98f9e909 100644 --- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -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 diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index 14c437d2b98..7852f492e1d 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -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? diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index c6522d5ada1..689a5dd45bc 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -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 diff --git a/lib/gitlab/routing.rb b/lib/gitlab/routing.rb index 5132177de51..632e2d87500 100644 --- a/lib/gitlab/routing.rb +++ b/lib/gitlab/routing.rb @@ -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 diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index c443af09075..62466c06194 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -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 diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index f6d625fa7f6..0aa01fc499a 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -21,7 +21,7 @@ feature 'Admin Groups', feature: true do scenario 'shows the visibility level radio populated with the group visibility_level value' do group = create(:group, :private) - visit edit_admin_group_path(group) + visit admin_group_edit_path(group) expect_selected_visibility(group.visibility_level) end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 30ded9202a4..a36bfd574cb 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -8,11 +8,11 @@ describe "Admin::Projects", feature: true do describe "GET /admin/projects" do before do - visit admin_namespaces_projects_path + visit admin_projects_path end it "is ok" do - expect(current_path).to eq(admin_namespaces_projects_path) + expect(current_path).to eq(admin_projects_path) end it "has projects list" do @@ -22,7 +22,7 @@ describe "Admin::Projects", feature: true do describe "GET /admin/projects/:id" do before do - visit admin_namespaces_projects_path + visit admin_projects_path click_link "#{@project.name}" end diff --git a/spec/features/security/admin_access_spec.rb b/spec/features/security/admin_access_spec.rb index fe8cd7b7602..e180ca53eb5 100644 --- a/spec/features/security/admin_access_spec.rb +++ b/spec/features/security/admin_access_spec.rb @@ -4,7 +4,7 @@ describe "Admin::Projects", feature: true do include AccessMatchers describe "GET /admin/projects" do - subject { admin_namespaces_projects_path } + subject { admin_projects_path } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_denied_for :user } diff --git a/spec/javascripts/lib/utils/custom_event_polyfill_spec.js.es6 b/spec/javascripts/lib/utils/custom_event_polyfill_spec.js.es6 new file mode 100644 index 00000000000..3645dd70c55 --- /dev/null +++ b/spec/javascripts/lib/utils/custom_event_polyfill_spec.js.es6 @@ -0,0 +1,43 @@ +//= require lib/utils/custom_event_polyfill + +describe('Custom Event Polyfill', () => { + it('should be defined', () => { + expect(window.CustomEvent).toBeDefined(); + }); + + it('should create a `CustomEvent` instance', () => { + const e = new window.CustomEvent('foo'); + + expect(e.type).toEqual('foo'); + expect(e.bubbles).toBe(false); + expect(e.cancelable).toBe(false); + expect(e.detail).toBeFalsy(); + }); + + it('should create a `CustomEvent` instance with a `details` object', () => { + const e = new window.CustomEvent('bar', { detail: { foo: 'bar' } }); + + expect(e.type).toEqual('bar'); + expect(e.bubbles).toBe(false); + expect(e.cancelable).toBe(false); + expect(e.detail.foo).toEqual('bar'); + }); + + it('should create a `CustomEvent` instance with a `bubbles` boolean', () => { + const e = new window.CustomEvent('bar', { bubbles: true }); + + expect(e.type).toEqual('bar'); + expect(e.bubbles).toBe(true); + expect(e.cancelable).toBe(false); + expect(e.detail).toBeFalsy(); + }); + + it('should create a `CustomEvent` instance with a `cancelable` boolean', () => { + const e = new window.CustomEvent('bar', { cancelable: true }); + + expect(e.type).toEqual('bar'); + expect(e.bubbles).toBe(false); + expect(e.cancelable).toBe(true); + expect(e.detail).toBeFalsy(); + }); +}); diff --git a/spec/lib/gitlab/allowable_spec.rb b/spec/lib/gitlab/allowable_spec.rb new file mode 100644 index 00000000000..87733d53e92 --- /dev/null +++ b/spec/lib/gitlab/allowable_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb new file mode 100644 index 00000000000..9376bce17a1 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb new file mode 100644 index 00000000000..40b96b1807b --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/common_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb new file mode 100644 index 00000000000..dccb29b5ef6 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb new file mode 100644 index 00000000000..4ddf04a8e11 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb new file mode 100644 index 00000000000..d61e5bbaa6b --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb new file mode 100644 index 00000000000..59a85b55f90 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb index 619ecbcba67..4639278ad45 100644 --- a/spec/lib/gitlab/ci/status/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb index 157302c65a8..2ce176a29d6 100644 --- a/spec/lib/gitlab/ci/status/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb index 120e121aae5..c2d74ca5cde 100644 --- a/spec/lib/gitlab/ci/status/extended_spec.rb +++ b/spec/lib/gitlab/ci/status/extended_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb index d5bd7f7102b..f92a1c149bf 100644 --- a/spec/lib/gitlab/ci/status/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -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( diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb index 0b3cb8168e6..9d527e6a7ef 100644 --- a/spec/lib/gitlab/ci/status/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb index 57c901c1202..d03f595d3c7 100644 --- a/spec/lib/gitlab/ci/status/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 21adee3f8e7..d665674bf70 100644 --- a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -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 - it 'links to the pipeline details page' do - expect(subject.details_path) - .to include "pipelines/#{pipeline.id}" + describe '#details_path' do + it 'links to the pipeline details page' do + expect(subject.details_path) + .to include "pipelines/#{pipeline.id}" + end end end diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index d6243940f2e..d4a2dc7fcc1 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb index 02e526e3de2..7e3383c307f 100644 --- a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb index c023f1872cc..9f47090d396 100644 --- a/spec/lib/gitlab/ci/status/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb index d4f7f4b3b70..94601648a8d 100644 --- a/spec/lib/gitlab/ci/status/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index f3259c6f23e..8814a7614a0 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 17929665c83..6f8721d30c2 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb index 9e261a3aa5f..90f9f615e0d 100644 --- a/spec/lib/gitlab/ci/status/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -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 diff --git a/spec/lib/gitlab/routing_spec.rb b/spec/lib/gitlab/routing_spec.rb new file mode 100644 index 00000000000..01d5acfc15b --- /dev/null +++ b/spec/lib/gitlab/routing_spec.rb @@ -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 diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index d4970e38f7c..7f39aff7639 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -899,21 +899,87 @@ describe Ci::Build, models: true do end end + 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 { is_expected.to be_cancelable } + end + end + + context 'when build is not cancelable' do + context 'when build is successful' do + before do + build.success! + end + + 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 - context 'when build is running' do - before do - build.run! + 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 - it { expect(build).not_to be_retryable } + context 'when build is canceled' do + before do + build.cancel! + end + + it { is_expected.to be_retryable } + end end - context 'when build is finished' do - before do - build.success! + 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 - it { expect(build).to be_retryable } + 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 diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 8158e71dd55..e78ae14b737 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -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 diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index f232761dba2..8fff38f7cda 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -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) } diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 1ec08c2a9d0..701f3323c0f 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -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 diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 615cfe3142b..6004bfdb7b7 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -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 diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 850b1a3cf1e..893c6827a91 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -50,9 +50,8 @@ describe Group, models: true do describe 'validations' do it { is_expected.to validate_presence_of :name } - it { is_expected.to validate_uniqueness_of(:name) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) } it { is_expected.to validate_presence_of :path } - it { is_expected.to validate_uniqueness_of(:path) } it { is_expected.not_to validate_presence_of :owner } end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 7f82e85563b..069c59fb5ca 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -6,20 +6,16 @@ describe Namespace, models: true do it { is_expected.to have_many :projects } it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_uniqueness_of(:name) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) } it { is_expected.to validate_length_of(:name).is_at_most(255) } it { is_expected.to validate_length_of(:description).is_at_most(255) } it { is_expected.to validate_presence_of(:path) } - it { is_expected.to validate_uniqueness_of(:path) } it { is_expected.to validate_length_of(:path).is_at_most(255) } it { is_expected.to validate_presence_of(:owner) } - describe "Mass assignment" do - end - describe "Respond to" do it { is_expected.to respond_to(:human_name) } it { is_expected.to respond_to(:to_param) } diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 5ce229a8cf2..964cded917c 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -475,7 +475,7 @@ describe API::Commits, api: true do expect(response).to have_http_status(400) expect(json_response['message']).to eq('Sorry, we cannot cherry-pick this commit automatically. - It may have already been cherry-pick, or a more recent commit may have updated some of its content.') + A cherry-pick may have already been performed with this commit, or a more recent commit may have updated some of its content.') end it 'returns 400 if you are not allowed to push to the target branch' do diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 69eeb45ed71..661b671301e 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -66,7 +66,8 @@ describe Admin::ProjectsController, "routing" do end it "to #show" do - expect(get("/admin/projects/gitlab")).to route_to('admin/projects#show', namespace_id: 'gitlab') + expect(get("/admin/projects/gitlab/gitlab-ce")).to route_to('admin/projects#show', namespace_id: 'gitlab', id: 'gitlab-ce') + expect(get("/admin/projects/gitlab/subgroup/gitlab-ce")).to route_to('admin/projects#show', namespace_id: 'gitlab/subgroup', id: 'gitlab-ce') end end @@ -119,3 +120,14 @@ describe Admin::HealthCheckController, "routing" do expect(get("/admin/health_check")).to route_to('admin/health_check#show') end end + +describe Admin::GroupsController, "routing" do + it "to #index" do + expect(get("/admin/groups")).to route_to('admin/groups#index') + end + + it "to #show" do + expect(get("/admin/groups/gitlab")).to route_to('admin/groups#show', id: 'gitlab') + expect(get("/admin/groups/gitlab/subgroup")).to route_to('admin/groups#show', id: 'gitlab/subgroup') + end +end |