diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 50f1f65e4e8e20c1c5a4e14cf38d253e887d91ed..ddc2c5f25423429711049119692f49f7db2a9f7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,8 +19,8 @@ variables: before_script: - bundle --version - - . scripts/utils.sh - - ./scripts/prepare_build.sh + - source scripts/utils.sh + - source scripts/prepare_build.sh stages: - prepare @@ -68,6 +68,13 @@ stages: - //@gitlab-org/gitlab-ee - //@gitlab/gitlab-ee +# Skip all jobs except the ones that begin with 'docs/'. +# Used for commits including ONLY documentation changes. +# https://docs.gitlab.com/ce/development/writing_documentation.html#testing +.except-docs: &except-docs + except: + - /^docs\/.*/ + .rspec-knapsack: &rspec-knapsack stage: test <<: *dedicated-runner @@ -91,11 +98,13 @@ stages: .rspec-knapsack-pg: &rspec-knapsack-pg <<: *rspec-knapsack <<: *use-pg + <<: *except-docs .rspec-knapsack-mysql: &rspec-knapsack-mysql <<: *rspec-knapsack <<: *use-mysql <<: *only-master-and-ee-or-mysql + <<: *except-docs .spinach-knapsack: &spinach-knapsack stage: test @@ -120,16 +129,19 @@ stages: .spinach-knapsack-pg: &spinach-knapsack-pg <<: *spinach-knapsack <<: *use-pg + <<: *except-docs .spinach-knapsack-mysql: &spinach-knapsack-mysql <<: *spinach-knapsack <<: *use-mysql <<: *only-master-and-ee-or-mysql + <<: *except-docs # Prepare and merge knapsack tests knapsack: <<: *knapsack-state <<: *dedicated-runner + <<: *except-docs stage: prepare script: - mkdir -p knapsack/${CI_PROJECT_NAME}/ @@ -156,6 +168,7 @@ update-knapsack: setup-test-env: <<: *use-pg <<: *dedicated-runner + <<: *except-docs stage: prepare script: - node --version @@ -243,6 +256,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql .exec: &exec <<: *ruby-static-analysis <<: *dedicated-runner + <<: *except-docs stage: test script: - bundle exec $CI_JOB_NAME @@ -250,6 +264,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql rubocop: <<: *ruby-static-analysis <<: *dedicated-runner + <<: *except-docs stage: test script: - bundle exec "rubocop --require rubocop-rspec" @@ -266,6 +281,7 @@ rake downtime_check: - master - tags - /^[\d-]+-stable(-ee)?$/ + - /^docs\/*/ rake ee_compat_check: <<: *exec @@ -296,10 +312,12 @@ rake ee_compat_check: rake pg db:migrate:reset: <<: *db-migrate-reset <<: *use-pg + <<: *except-docs rake mysql db:migrate:reset: <<: *db-migrate-reset <<: *use-mysql + <<: *except-docs .db-rollback: &db-rollback stage: test @@ -311,10 +329,12 @@ rake mysql db:migrate:reset: rake pg db:rollback: <<: *db-rollback <<: *use-pg + <<: *except-docs rake mysql db:rollback: <<: *db-rollback <<: *use-mysql + <<: *except-docs .db-seed_fu: &db-seed_fu stage: test @@ -336,14 +356,17 @@ rake mysql db:rollback: rake pg db:seed_fu: <<: *db-seed_fu <<: *use-pg + <<: *except-docs rake mysql db:seed_fu: <<: *db-seed_fu <<: *use-mysql + <<: *except-docs rake gitlab:assets:compile: stage: test <<: *dedicated-runner + <<: *except-docs dependencies: [] variables: NODE_ENV: "production" @@ -367,6 +390,7 @@ rake karma: stage: test <<: *use-pg <<: *dedicated-runner + <<: *except-docs variables: BABEL_ENV: "coverage" script: @@ -447,6 +471,7 @@ coverage: stage: post-test services: [] <<: *dedicated-runner + <<: *except-docs variables: SETUP_DB: "false" USE_BUNDLE_INSTALL: "true" @@ -462,6 +487,7 @@ coverage: lint:javascript: <<: *dedicated-runner + <<: *except-docs stage: test before_script: [] script: @@ -469,6 +495,7 @@ lint:javascript: lint:javascript:report: <<: *dedicated-runner + <<: *except-docs stage: post-test before_script: [] script: diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 241d90a204b964f14b0fafd88f30e9b81ecabfcd..66e1e0e20b340d2095caa1e67191d4513a2b3e79 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,3 +1,17 @@ +Please read this! + +Before opening a new issue, make sure to search for keywords in the issues +filtered by the "regression" or "bug" label: + +- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=regression +- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=bug + +and verify the issue you're about to submit isn't a duplicate. + +Please remove this notice if you're confident your issue isn't a duplicate. + +------ + ### Summary (Summarize the bug encountered concisely) @@ -56,3 +70,5 @@ logs, and code as it's very hard to read otherwise.) ### Possible fixes (If you can, link to the line of code that might be responsible for the problem) + +/label ~bug diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature Proposal.md index 2636010e2fb3bca23947aa890891d10c40348f64..d96c9ad59e0b5e0c2d148be157f79c8790243dc7 100644 --- a/.gitlab/issue_templates/Feature Proposal.md +++ b/.gitlab/issue_templates/Feature Proposal.md @@ -1,3 +1,16 @@ +Please read this! + +Before opening a new issue, make sure to search for keywords in the issues +filtered by the "feature proposal" label: + +- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=feature+proposal + +and verify the issue you're about to submit isn't a duplicate. + +Please remove this notice if you're confident your issue isn't a duplicate. + +------ + ### Description (Include problem, use cases, benefits, and/or goals) @@ -15,3 +28,5 @@ 3. How does someone use this During implementation, this can then be copied and used as a starter for the documentation.) + +/label ~"feature proposal" diff --git a/.rubocop.yml b/.rubocop.yml index e73500be2a92666d3951f907284c10ab84fc82cd..8c43f6909cf24aaba4f65e5593e02b03b4f067e5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -983,10 +983,12 @@ RSpec/ExpectActual: # Checks the file and folder naming of the spec file. RSpec/FilePath: - Enabled: false - CustomTransform: - RuboCop: rubocop - RSpec: rspec + Enabled: true + IgnoreMethods: true + Exclude: + - 'qa/**/*' + - 'spec/javascripts/fixtures/*' + - 'spec/requests/api/v3/*' # Checks if there are focused specs. RSpec/Focus: diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 1d0ba9ea182b0f7354f3daf12120744ec5e0c2f8..267577d47e497a0630bc454b3f74c4fd9a10ced4 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.4.0 +0.4.1 diff --git a/Gemfile.lock b/Gemfile.lock index 527076287487696ded1ed1bcc77eaf3e1c238763..038d1f746b35d8d3002d6e2883109390c6297d51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -330,7 +330,7 @@ GEM grape-entity (0.6.0) activesupport multi_json (>= 1.3.2) - grpc (1.1.2) + grpc (1.2.5) google-protobuf (~> 3.1) googleauth (~> 0.5.1) haml (4.0.7) diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss index b2102d2fbc501f80720e8acfb83f7e6968c19b12..9159927ed8b3a415c6bbe151f4a9951b225e48ff 100644 --- a/app/assets/stylesheets/framework/awards.scss +++ b/app/assets/stylesheets/framework/awards.scss @@ -227,8 +227,8 @@ .award-control-icon-positive, .award-control-icon-super-positive { position: absolute; - left: 7px; - bottom: 9px; + left: 11px; + bottom: 7px; opacity: 0; @include transition(opacity, transform); } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index a95ce4cc963e96ba50bb17f00638e817b5948bd0..1dd0e5ab581b4d6bf8dcf38b8088a3865df3fbe0 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -61,11 +61,13 @@ .file-content { background: $white-light; - &.image_file { + &.image_file, + &.video { background: $file-image-bg; text-align: center; - img { + img, + video { padding: 20px; max-width: 80%; } diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb index a8c0937569c3f0fb152e9b3cadc648fa162a2b6a..be2e6c7f193ea807a29f23a3acfd941987401b1f 100644 --- a/app/controllers/concerns/service_params.rb +++ b/app/controllers/concerns/service_params.rb @@ -38,6 +38,7 @@ module ServiceParams :new_issue_url, :notify, :notify_only_broken_pipelines, + :notify_only_default_branch, :password, :priority, :project_key, diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index fb2a483773542737f88289cc8e232783e57c08bc..1ff08cce8cba675a6e8603aa836060f74c75a377 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -5,7 +5,7 @@ module Projects before_action :authorize_admin_project! layout "project_settings" - + def show @hooks = @project.hooks @hook = ProjectHook.new diff --git a/app/controllers/unicorn_test_controller.rb b/app/controllers/unicorn_test_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..b7a1a046be0b785d7ec7b9b4cb0b1897ed381acd --- /dev/null +++ b/app/controllers/unicorn_test_controller.rb @@ -0,0 +1,12 @@ +if Rails.env.test? + class UnicornTestController < ActionController::Base + def pid + render plain: Process.pid.to_s + end + + def kill + Process.kill(params[:signal], Process.pid) + render plain: 'Bye!' + end + end +end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 5f5c76d3722e8ea6a9299585278e97f0e9f6800c..960111ca045439498959f5d8d04cd090ee61b2c5 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -10,11 +10,12 @@ module EventsHelper 'deleted' => 'icon_trash_o' }.freeze - def link_to_author(event) + def link_to_author(event, self_added: false) author = event.author if author - link_to author.name, user_path(author.username), title: author.name + name = self_added ? 'You' : author.name + link_to name, user_path(author.username), title: name else event.author_name end diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 0781874d7fc7323e9c8cbfe3e1daa40e76c46d4b..b241a14740b34707d0e61acb3c54cc876ed17bd1 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -74,7 +74,7 @@ module MarkupHelper context[:project] ||= @project html = markdown_unsafe(text, context) - banzai_postprocess(html, context) + prepare_for_rendering(html, context) end def markdown_field(object, field) @@ -82,13 +82,13 @@ module MarkupHelper return '' unless object.present? html = Banzai.render_field(object, field) - banzai_postprocess(html, object.banzai_render_context(field)) + prepare_for_rendering(html, object.banzai_render_context(field)) end def markup(file_name, text, context = {}) context[:project] ||= @project html = context.delete(:rendered) || markup_unsafe(file_name, text, context) - banzai_postprocess(html, context) + prepare_for_rendering(html, context) end def render_wiki_content(wiki_page) @@ -107,14 +107,14 @@ module MarkupHelper wiki_page.formatted_content.html_safe end - banzai_postprocess(html, context) + prepare_for_rendering(html, context) end def markup_unsafe(file_name, text, context = {}) return '' unless text.present? if gitlab_markdown?(file_name) - Hamlit::RailsHelpers.preserve(markdown_unsafe(text, context)) + markdown_unsafe(text, context) elsif asciidoc?(file_name) asciidoc_unsafe(text) elsif plain?(file_name) @@ -225,8 +225,7 @@ module MarkupHelper Gitlab::OtherMarkup.render(file_name, text) end - # Calls Banzai.post_process with some common context options - def banzai_postprocess(html, context = {}) + def prepare_for_rendering(html, context = {}) return '' unless html.present? context.merge!( @@ -239,7 +238,9 @@ module MarkupHelper requested_path: @path ) - Banzai.post_process(html, context) + html = Banzai.post_process(html, context) + + Hamlit::RailsHelpers.preserve(html) end extend self diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 4f5adf623f2f6ae11a40e602ce423cb4bde3736b..f19e2f9db9c7c0b934e2bc22cde71b69c511caf3 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -13,13 +13,13 @@ module TodosHelper def todo_action_name(todo) case todo.action - when Todo::ASSIGNED then 'assigned you' - when Todo::MENTIONED then 'mentioned you on' + when Todo::ASSIGNED then todo.self_added? ? 'assigned' : 'assigned you' + when Todo::MENTIONED then "mentioned #{todo_action_subject(todo)} on" when Todo::BUILD_FAILED then 'The build failed for' when Todo::MARKED then 'added a todo for' - when Todo::APPROVAL_REQUIRED then 'set you as an approver for' + when Todo::APPROVAL_REQUIRED then "set #{todo_action_subject(todo)} as an approver for" when Todo::UNMERGEABLE then 'Could not merge' - when Todo::DIRECTLY_ADDRESSED then 'directly addressed you on' + when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on" end end @@ -148,6 +148,10 @@ module TodosHelper private + def todo_action_subject(todo) + todo.self_added? ? 'yourself' : 'you' + end + def show_todo_state?(todo) (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state) end diff --git a/app/models/blob.rb b/app/models/blob.rb index 1694f4e6dc9654b3b914e997b6278f853210e76e..934dd2b9a4f7cc83917b9864cd6a946be3e7540c 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -28,6 +28,8 @@ class Blob < SimpleDelegator BlobViewer::Sketch, BlobViewer::Balsamiq, + BlobViewer::Video, + BlobViewer::PDF, BlobViewer::BinarySTL, diff --git a/app/models/blob_viewer/video.rb b/app/models/blob_viewer/video.rb new file mode 100644 index 0000000000000000000000000000000000000000..057f9fe516f02a2e4da96ae4dca57fa9782efd3b --- /dev/null +++ b/app/models/blob_viewer/video.rb @@ -0,0 +1,12 @@ +module BlobViewer + class Video < Base + include Rich + include ClientSide + + self.partial_name = 'video' + self.extensions = UploaderHelper::VIDEO_EXT + self.binary = true + self.switcher_icon = 'film' + self.switcher_title = 'video' + end +end diff --git a/app/models/label.rb b/app/models/label.rb index d8b0e250732731ac00c74cb44b112dc648cf43fd..ddddb6bdf8fa60471c77b295b8bd8755cdfc8dd0 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -34,6 +34,7 @@ class Label < ActiveRecord::Base scope :templates, -> { where(template: true) } scope :with_title, ->(title) { where(title: title) } + scope :on_project_boards, ->(project_id) { joins(lists: :board).merge(List.movable).where(boards: { project_id: project_id }) } def self.prioritized(project) joins(:priorities) diff --git a/app/models/member.rb b/app/models/member.rb index 97fba501759e95ff94b308974f97a04fdb7fcac6..7228e82e9781384bd6d232b5087d767dd774195a 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -154,6 +154,11 @@ class Member < ActiveRecord::Base def add_users(source, users, access_level, current_user: nil, expires_at: nil) return [] unless users.present? + # Collect all user ids into separate array + # so we can use single sql query to get user objects + user_ids = users.select { |user| user =~ /\A\d+\Z/ } + users = users - user_ids + User.where(id: user_ids) + self.transaction do users.map do |user| add_user( diff --git a/app/models/todo.rb b/app/models/todo.rb index da3fa7277c2c54488dddec023703c24717f942cf..b011001b235e84177b3ee0373634ba02d203d377 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -84,6 +84,10 @@ class Todo < ActiveRecord::Base action == BUILD_FAILED end + def assigned? + action == ASSIGNED + end + def action_name ACTION_NAMES[action] end @@ -117,6 +121,14 @@ class Todo < ActiveRecord::Base end end + def self_added? + author == user + end + + def self_assigned? + assigned? && self_added? + end + private def keep_around_commit diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb index d5735f13c1ea5b881156538e8de847cb745b63e0..e73b1a4361ac68c3fa24d854c875475ac4f59839 100644 --- a/app/services/boards/issues/move_service.rb +++ b/app/services/boards/issues/move_service.rb @@ -61,7 +61,7 @@ module Boards if moving_to_list.movable? moving_from_list.label_id else - project.boards.joins(:lists).merge(List.movable).pluck(:label_id) + Label.on_project_boards(project.id).pluck(:label_id) end Array(label_ids).compact diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb index 49d45ec9dbd3920bbd7baad50ab8fa1498174304..6aeebc26685aead3731abb1c8c580893c24e3bf7 100644 --- a/app/services/slash_commands/interpret_service.rb +++ b/app/services/slash_commands/interpret_service.rb @@ -330,6 +330,28 @@ module SlashCommands @updates[:target_branch] = branch_name if project.repository.branch_names.include?(branch_name) end + desc 'Move issue from one column of the board to another' + params '~"Target column"' + condition do + issuable.is_a?(Issue) && + current_user.can?(:"update_#{issuable.to_ability_name}", issuable) && + issuable.project.boards.count == 1 + end + command :board_move do |target_list_name| + label_ids = find_label_ids(target_list_name) + + if label_ids.size == 1 + label_id = label_ids.first + + # Ensure this label corresponds to a list on the board + next unless Label.on_project_boards(issuable.project_id).where(id: label_id).exists? + + @updates[:remove_label_ids] = + issuable.labels.on_project_boards(issuable.project_id).where.not(id: label_id).pluck(:id) + @updates[:add_label_ids] = [label_id] + end + end + def find_label_ids(labels_param) label_ids_by_reference = extract_references(labels_param, :label).map(&:id) labels_ids_by_name = LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute.select(:id) diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index d0c12aa57aefbe593e332aaa639bf8b28b721309..38fd053ae657ac62715c00039b29d1a8df377296 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -9,7 +9,7 @@ .title-item.author-name - if todo.author - = link_to_author(todo) + = link_to_author(todo, self_added: todo.self_added?) - else (removed) @@ -22,6 +22,10 @@ - else (removed) + - if todo.self_assigned? + .title-item.action-name + to yourself + .title-item · diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index f93b6b63426eca6a2335e03f9c870e162fc257b9..b20e3a22133372674102095e2fb240a295d547f3 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -27,8 +27,7 @@ .row .col-md-8 .documentation-index - = preserve do - = markdown(@help_index) + = markdown(@help_index) .col-md-4 .panel.panel-default .panel-heading diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml index 41d42740f61a5f9fa5463187b871dfacc630d448..2bab22e125d96de5e4e6a71b18c69ff18966403d 100644 --- a/app/views/projects/_wiki.html.haml +++ b/app/views/projects/_wiki.html.haml @@ -2,8 +2,7 @@ %div{ class: container_class } .wiki-holder.prepend-top-default.append-bottom-default .wiki - = preserve do - = render_wiki_content(@wiki_home) + = render_wiki_content(@wiki_home) - else - can_create_wiki = can?(current_user, :create_wiki, @project) .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] } diff --git a/app/views/projects/blob/viewers/_video.html.haml b/app/views/projects/blob/viewers/_video.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..595a890a27dc40192d0a65a7f35a3e3d7a21ca68 --- /dev/null +++ b/app/views/projects/blob/viewers/_video.html.haml @@ -0,0 +1,2 @@ +.file-content.video + %video{ src: blob_raw_url, controls: true, data: { setup: '{}' } } diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 4d56aa214e2f35f1922f7aec44e550a980402e78..2a871966aa832d4dd79bfc2166a9bec7ed09ccad 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -58,8 +58,7 @@ - if @issue.description.present? .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } .wiki - = preserve do - = markdown_field(@issue, :description) + = markdown_field(@issue, :description) %textarea.hidden.js-task-list-field = @issue.description = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 683cb8a5a27ae9db5896cee90be6073a7470d9a6..8a390cf870044973f247fd280a35d0ef4333c04c 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -6,8 +6,7 @@ - if @merge_request.description.present? .description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' } .wiki - = preserve do - = markdown_field(@merge_request, :description) + = markdown_field(@merge_request, :description) %textarea.hidden.js-task-list-field = @merge_request.description diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 33bbbd9a3f88ce4c0f6cd8bf8ac23c3a1371598a..4b692aba11cf9eec00508a80e8d38a338b040efd 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -43,8 +43,7 @@ - if @milestone.description.present? .description .wiki - = preserve do - = markdown_field(@milestone, :description) + = markdown_field(@milestone, :description) - if can?(current_user, :read_issue, @project) && @milestone.total_items_count(current_user).zero? .alert.alert-success.prepend-top-default diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 7cf604bb77255019f6c9982db53e2b3b9e8fcfb7..7afccb3900ac91df0b35d98b859decf2711e5e7e 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -75,8 +75,7 @@ = icon('trash-o', class: 'danger-highlight') .note-body{ class: note_editable ? 'js-task-list-container' : '' } .note-text.md - = preserve do - = note.redacted_note_html + = note.redacted_note_html = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note_editable .original-note-content.hidden{ data: { post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } } diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 451e011a4b8216d4ea253e4c8bc8aec2bd07957f..4c4f3655b97f9bf3b26dcb5d7faef5824f6b7d30 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -24,8 +24,7 @@ - if release && release.description.present? .description.prepend-top-default .wiki - = preserve do - = markdown_field(release, :description) + = markdown_field(release, :description) .row-fixed-content.controls = render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name] diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 1c4135c8a5400e4b2ca2e90cce19e7080a585ca8..e996ae3e4fc3198897d2ec904afceb4e144d96e3 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -38,7 +38,6 @@ - if @release.description.present? .description .wiki - = preserve do - = markdown_field(@release, :description) + = markdown_field(@release, :description) - else This tag has no release notes. diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 3609461b721364f56939855741a82565da994bee..c00967546aa6f8a0475152632537285c42005048 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -27,7 +27,6 @@ .wiki-holder.prepend-top-default.append-bottom-default .wiki - = preserve do - = render_wiki_content(@page) + = render_wiki_content(@page) = render 'sidebar' diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index fc4385865a46ef7565bbde185413a3350fd2be04..b4bc8982c05cceb4cbb38cd4ead8f755211ececc 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -8,7 +8,6 @@ .pull-right ##{issue.iid} - if issue.description.present? .description.term - = preserve do - = search_md_sanitize(issue, :description) + = search_md_sanitize(issue, :description) %span.light #{issue.project.name_with_namespace} diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 9b583285d02d701b100330a84be46349102bdba0..1a5499e4d5864e6c068f78bf75dfd95feffa02ab 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -9,7 +9,6 @@ .pull-right= merge_request.to_reference - if merge_request.description.present? .description.term - = preserve do - = search_md_sanitize(merge_request, :description) + = search_md_sanitize(merge_request, :description) %span.light #{merge_request.project.name_with_namespace} diff --git a/app/views/search/results/_milestone.html.haml b/app/views/search/results/_milestone.html.haml index 9664f65a36e016140be5de9f152991af07d3fa4f..2daa96e34d1d8f45bdc534dae9a98af7dc57d81e 100644 --- a/app/views/search/results/_milestone.html.haml +++ b/app/views/search/results/_milestone.html.haml @@ -5,5 +5,4 @@ - if milestone.description.present? .description.term - = preserve do - = search_md_sanitize(milestone, :description) + = search_md_sanitize(milestone, :description) diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index f3701b89bb4709f76f3922800ddc56ae52aa97c9..a7e178dfa713157d84698a9dcfc982695e553847 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -22,5 +22,4 @@ .note-search-result .term - = preserve do - = search_md_sanitize(note, :note) + = search_md_sanitize(note, :note) diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 9c5053dace5df1da26b7447adffab1dc17e7eb28..b200e5fc5287a423ff8c044aaee80b42ab4be7d2 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -4,8 +4,7 @@ = render "projects/services/#{@service.to_param}/help", subject: subject - elsif @service.help.present? .well - = preserve do - = markdown @service.help + = markdown @service.help .service-settings .form-group diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index 774d20fb5badacd341999bc043b67f1c9bca4baf..5e8a2a0f5d8107464956ef90c10f30deb2d3279e 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -64,7 +64,7 @@ %span.remaining-days= remaining_days - if !project || can?(current_user, :read_issue, project) - .block + .block.issues .sidebar-collapsed-icon %strong = icon('hashtag', 'aria-hidden': 'true') @@ -85,7 +85,7 @@ Closed: = milestone.issues_visible_to_user(current_user).closed.count - .block + .block.merge-requests .sidebar-collapsed-icon %strong = icon('exclamation', 'aria-hidden': 'true') diff --git a/changelogs/unreleased/28020-improve-todo-list-when-comes-from-yourself.yml b/changelogs/unreleased/28020-improve-todo-list-when-comes-from-yourself.yml new file mode 100644 index 0000000000000000000000000000000000000000..14aecc35bd2ce679a8d3fc7c462f8c69919a587d --- /dev/null +++ b/changelogs/unreleased/28020-improve-todo-list-when-comes-from-yourself.yml @@ -0,0 +1,4 @@ +--- +title: Improve text on todo list when the todo action comes from yourself +merge_request: 10594 +author: Jacopo Beschi @jacopo-beschi diff --git a/changelogs/unreleased/28457-slash-command-board-move.yml b/changelogs/unreleased/28457-slash-command-board-move.yml new file mode 100644 index 0000000000000000000000000000000000000000..cec0f89ed912acbaebde9e68721fbbde67a9208d --- /dev/null +++ b/changelogs/unreleased/28457-slash-command-board-move.yml @@ -0,0 +1,4 @@ +--- +title: Add board_move slash command +merge_request: 10433 +author: Alex Sanford diff --git a/changelogs/unreleased/add_index_on_ci_runners_contacted_at.yml b/changelogs/unreleased/add_index_on_ci_runners_contacted_at.yml new file mode 100644 index 0000000000000000000000000000000000000000..10c3206c2ff95ca86999b966636718d48c519f2c --- /dev/null +++ b/changelogs/unreleased/add_index_on_ci_runners_contacted_at.yml @@ -0,0 +1,4 @@ +--- +title: Add index on ci_runners.contacted_at +merge_request: 10876 +author: blackst0ne diff --git a/changelogs/unreleased/dm-video-viewer.yml b/changelogs/unreleased/dm-video-viewer.yml new file mode 100644 index 0000000000000000000000000000000000000000..1c42b16e9674da8ce9ca4ac644987626aaf9d90e --- /dev/null +++ b/changelogs/unreleased/dm-video-viewer.yml @@ -0,0 +1,4 @@ +--- +title: Display video blobs in-line like images +merge_request: +author: diff --git a/changelogs/unreleased/pages-0-4-1.yml b/changelogs/unreleased/pages-0-4-1.yml new file mode 100644 index 0000000000000000000000000000000000000000..fbc78a36cae716c7f5fb52a9b7799c56b09bfebe --- /dev/null +++ b/changelogs/unreleased/pages-0-4-1.yml @@ -0,0 +1,4 @@ +--- +title: Use GitLab Pages v0.4.1 +merge_request: +author: diff --git a/changelogs/unreleased/zj-accept-default-branch-param.yml b/changelogs/unreleased/zj-accept-default-branch-param.yml new file mode 100644 index 0000000000000000000000000000000000000000..8f6fa8a63867f050c3b2350863f6a765aa9d4fbc --- /dev/null +++ b/changelogs/unreleased/zj-accept-default-branch-param.yml @@ -0,0 +1,4 @@ +--- +title: Ensure the chat notifications service properly saves the "Notify only default branch" setting +merge_request: 10959 +author: diff --git a/config/initializers/rspec_profiling.rb b/config/initializers/rspec_profiling.rb index b909cc5b9a4fb775cee5716b9983524ee2fb24cd..a7efd74f09e93d9afdb62f1604807e54d3611432 100644 --- a/config/initializers/rspec_profiling.rb +++ b/config/initializers/rspec_profiling.rb @@ -36,10 +36,10 @@ if Rails.env.test? RspecProfiling::Collectors::PSQL.prepend(RspecProfilingExt::PSQL) config.collector = RspecProfiling::Collectors::PSQL end - end - if ENV.has_key?('CI') && ENV['GITLAB_DATABASE'] == 'postgresql' - RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) - RspecProfiling::Run.prepend(RspecProfilingExt::Run) + if ENV.key?('CI') + RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) + RspecProfiling::Run.prepend(RspecProfilingExt::Run) + end end end diff --git a/config/routes.rb b/config/routes.rb index 1da226a3b575b54aefbe8343b4c60261e55dabe8..2584981bb040e3448f462fe7516e9fd9b7d40301 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,5 +99,7 @@ Rails.application.routes.draw do end end + draw :test if Rails.env.test? + get '*unmatched_route', to: 'application#route_not_found' end diff --git a/config/routes/test.rb b/config/routes/test.rb new file mode 100644 index 0000000000000000000000000000000000000000..ac477cdbbbc2c69a892df513b05ed6665686569a --- /dev/null +++ b/config/routes/test.rb @@ -0,0 +1,2 @@ +get '/unicorn_test/pid' => 'unicorn_test#pid' +post '/unicorn_test/kill' => 'unicorn_test#kill' diff --git a/db/migrate/20170426181740_add_index_on_ci_runners_contacted_at.rb b/db/migrate/20170426181740_add_index_on_ci_runners_contacted_at.rb new file mode 100644 index 0000000000000000000000000000000000000000..879825a1934a227eaf2af36350407396aeefbbec --- /dev/null +++ b/db/migrate/20170426181740_add_index_on_ci_runners_contacted_at.rb @@ -0,0 +1,19 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddIndexOnCiRunnersContactedAt < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :ci_runners, :contacted_at + end + + def down + remove_concurrent_index :ci_runners, :contacted_at if index_exists?(:ci_runners, :contacted_at) + end +end diff --git a/db/schema.rb b/db/schema.rb index 49d7c9966614af13421b9b69a231df1b06fc23a3..b938657a186557b164103d34ecc6dc8407b4da68 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170426175636) do +ActiveRecord::Schema.define(version: 20170426181740) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -296,6 +296,7 @@ ActiveRecord::Schema.define(version: 20170426175636) do t.boolean "locked", default: false, null: false end + add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree add_index "ci_runners", ["is_shared"], name: "index_ci_runners_on_is_shared", using: :btree add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index f6027b2f99eb1cd7455c8640edc480c3805b609e..725fc1f6076e02822ac346d4b8a8dbc979937b38 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -65,14 +65,14 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Example: 'Paris' or 'Acme, Ltd.' label: 'LDAP' - + # Example: 'ldap.mydomain.com' host: '_your_ldap_server' # This port is an example, it is sometimes different but it is always an integer and not a string port: 389 - uid: 'sAMAccountName' + uid: 'sAMAccountName' # This should be the attribute, not the value that maps to uid. method: 'plain' # "tls" or "ssl" or "plain" - + # Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md index 166a10293c37f4a23f8e29836f33f2f5003e5297..2814c18e0b63e1fc3b528ca7e9494c55d54a1ab9 100644 --- a/doc/development/writing_documentation.md +++ b/doc/development/writing_documentation.md @@ -70,3 +70,27 @@ All the docs follow the same [styleguide](doc_styleguide.md). ### Markdown Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future. + +## Testing + +We try to treat documentation as code, thus have implemented some testing. +Currently, the following tests are in place: + +1. `docs:check:links`: Check that all internal (relative) links work correctly +1. `docs:check:apilint`: Check that the API docs follow some conventions + +If your contribution contains **only** documentation changes, you can speed up +the CI process by prepending to the name of your branch: `docs/`. For example, +a valid name would be `docs/update-api-issues` and it will run only the docs +tests. If the name is `docs-update-api-issues`, the whole test suite will run +(including docs). + +--- + +When you submit a merge request to GitLab Community Edition (CE), there is an +additional job called `rake ee_compat_check` that runs against Enterprise +Edition (EE) and checks if your changes can apply cleanly to the EE codebase. +If that job fails, read the instructions in the job log for what to do next. +Contributors do not need to submit their changes to EE, GitLab Inc. employees +on the other hand need to make sure that their changes apply cleanly to both +CE and EE. diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md index a4935f66cbde76926879c80f113b2be2f566eabb..a954840b8a674fcd41b52a608cd2b9cddf144373 100644 --- a/doc/user/admin_area/monitoring/health_check.md +++ b/doc/user/admin_area/monitoring/health_check.md @@ -100,7 +100,7 @@ On failure, the endpoint will return a `500` HTTP status code. On success, the e will return a valid successful HTTP status code, and a `success` message. Ideally your uptime monitoring should look for the success message. -[ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888 +[ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10416 [ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888 [pingdom]: https://www.pingdom.com [nagios-health]: https://nagios-plugins.org/doc/man/check_http.html diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md index 45176fde9db860568eae8c1e8fb75a84ced5c43d..08452ca75cd3d7a88b9aa5905fd5e35fc37ab8ab 100644 --- a/doc/user/project/slash_commands.md +++ b/doc/user/project/slash_commands.md @@ -36,3 +36,4 @@ do. | `/remove_time_spent` | Remove time spent | | `/target_branch <Branch Name>` | Set target branch for current merge request | | `/award :emoji:` | Toggle award for :emoji: | +| `/board_move ~column` | Move issue to column on the board | diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index de737cdc82364555fda2c0f09c77c19746a1cd1c..f19fa1c7600a8803ad25e191d41099c55f20d0e6 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps expect(response_headers['Content-Type']).to have_content("application/atom+xml") expect(body).to have_selector("title", text: "#{@project.name}:master commits") expect(body).to have_selector("author email", text: commit.author_email) - expect(body).to have_selector("entry summary", text: commit.description[0..10].delete("\r")) + expect(body).to have_selector("entry summary", text: commit.description[0..10].delete("\r\n")) end step 'I click on tag link' do diff --git a/lib/tasks/brakeman.rake b/lib/tasks/brakeman.rake index 2301ec9b2288c16b0a32701e40c679a8f107dfcd..99b3168d9eb85c7c43073918f3222c9a410dda1c 100644 --- a/lib/tasks/brakeman.rake +++ b/lib/tasks/brakeman.rake @@ -2,7 +2,7 @@ desc 'Security check via brakeman' task :brakeman do # We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge # requests are welcome! - if system(*%w(brakeman --no-progress --skip-files lib/backup/repository.rb -w3 -z)) + if system(*%w(brakeman --no-progress --skip-files lib/backup/repository.rb,app/controllers/unicorn_test_controller.rb -w3 -z)) puts 'Security check succeed' else puts 'Security check failed' diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh old mode 100755 new mode 100644 index fd173c0ba88dce6bcfae83cd38eb0b3210ca69df..c727a0e2d884e2d7b574771277a011195fc4352c --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,5 +1,3 @@ -#!/bin/sh - . scripts/utils.sh export SETUP_DB=${SETUP_DB:-true} @@ -32,7 +30,7 @@ sed -i 's/localhost/redis/g' config/resque.yml cp config/gitlab.yml.example config/gitlab.yml if [ "$USE_BUNDLE_INSTALL" != "false" ]; then - retry bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check + bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check fi # Only install knapsack after bundle install! Otherwise oddly some native diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb deleted file mode 100644 index 44e011fd3a85b5cbb0948c55a279d34617f2b65e..0000000000000000000000000000000000000000 --- a/spec/controllers/blob_controller_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'spec_helper' - -describe Projects::BlobController do - let(:project) { create(:project, :repository) } - let(:user) { create(:user) } - - before do - sign_in(user) - - project.team << [user, :master] - - allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) - allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) - controller.instance_variable_set(:@project, project) - end - - describe "GET show" do - render_views - - before do - get(:show, - namespace_id: project.namespace, - project_id: project, - id: id) - end - - context "valid branch, valid file" do - let(:id) { 'master/README.md' } - it { is_expected.to respond_with(:success) } - end - - context "valid branch, invalid file" do - let(:id) { 'master/invalid-path.rb' } - it { is_expected.to respond_with(:not_found) } - end - - context "invalid branch, valid file" do - let(:id) { 'invalid-branch/README.md' } - it { is_expected.to respond_with(:not_found) } - end - - context "binary file" do - let(:id) { 'binary-encoding/encoding/binary-1.bin' } - it { is_expected.to respond_with(:success) } - end - end - - describe 'GET show with tree path' do - render_views - - before do - get(:show, - namespace_id: project.namespace, - project_id: project, - id: id) - controller.instance_variable_set(:@blob, nil) - end - - context 'redirect to tree' do - let(:id) { 'markdown/doc' } - it 'redirects' do - expect(subject). - to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") - end - end - end -end diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d321bfcea9d81bd455ae3f73c3e91c0112a237b2 --- /dev/null +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Oauth::AuthorizationsController do + let(:user) { create(:user) } + + let(:doorkeeper) do + Doorkeeper::Application.create( + name: "MyApp", + redirect_uri: 'http://example.com', + scopes: "") + end + + let(:params) do + { + response_type: "code", + client_id: doorkeeper.uid, + redirect_uri: doorkeeper.redirect_uri, + state: 'state' + } + end + + before do + sign_in(user) + end + + describe 'GET #new' do + context 'without valid params' do + it 'returns 200 code and renders error view' do + get :new + + expect(response).to have_http_status(200) + expect(response).to render_template('doorkeeper/authorizations/error') + end + end + + context 'with valid params' do + it 'returns 200 code and renders view' do + get :new, params + + expect(response).to have_http_status(200) + expect(response).to render_template('doorkeeper/authorizations/new') + end + + it 'deletes session.user_return_to and redirects when skip authorization' do + request.session['user_return_to'] = 'http://example.com' + allow(controller).to receive(:skip_authorization?).and_return(true) + + get :new, params + + expect(request.session['user_return_to']).to be_nil + expect(response).to have_http_status(302) + end + end + end +end diff --git a/spec/controllers/profiles/personal_access_tokens_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb similarity index 100% rename from spec/controllers/profiles/personal_access_tokens_spec.rb rename to spec/controllers/profiles/personal_access_tokens_controller_spec.rb diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 0fd09d156c49ea00dc20edc8a4cbbe70070b34d4..3b3caa9d3e63b4d4fdae61587dd3ae8b8fbe7487 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -3,6 +3,57 @@ require 'rails_helper' describe Projects::BlobController do let(:project) { create(:project, :public, :repository) } + describe "GET show" do + render_views + + context 'with file path' do + before do + get(:show, + namespace_id: project.namespace, + project_id: project, + id: id) + end + + context "valid branch, valid file" do + let(:id) { 'master/README.md' } + it { is_expected.to respond_with(:success) } + end + + context "valid branch, invalid file" do + let(:id) { 'master/invalid-path.rb' } + it { is_expected.to respond_with(:not_found) } + end + + context "invalid branch, valid file" do + let(:id) { 'invalid-branch/README.md' } + it { is_expected.to respond_with(:not_found) } + end + + context "binary file" do + let(:id) { 'binary-encoding/encoding/binary-1.bin' } + it { is_expected.to respond_with(:success) } + end + end + + context 'with tree path' do + before do + get(:show, + namespace_id: project.namespace, + project_id: project, + id: id) + controller.instance_variable_set(:@blob, nil) + end + + context 'redirect to tree' do + let(:id) { 'markdown/doc' } + it 'redirects' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") + end + end + end + end + describe 'GET diff' do let(:user) { create(:user) } diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todos_controller_spec.rb similarity index 100% rename from spec/controllers/projects/todo_controller_spec.rb rename to spec/controllers/projects/todos_controller_spec.rb diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 55e10a1a89bbe7d47e060fe36d7b80a7e8e6e6d8..7a2987e815d47dcbea0b2ebcab661349f7fd583b 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -53,7 +53,7 @@ describe "User Feed", feature: true do end it 'has XHTML summaries in issue descriptions' do - expect(body).to match /we have a bug!<\/p>\n\n<hr ?\/>\n\n<p dir="auto">I guess/ + expect(body).to match /<hr ?\/>/ end it 'has XHTML summaries in notes' do diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb index 5e19907eef9047b2bb638dd1b0a5118c880381ab..b4fc0edbde822b81001546eb0105d71bf5947d33 100644 --- a/spec/features/projects/milestones/milestone_spec.rb +++ b/spec/features/projects/milestones/milestone_spec.rb @@ -78,11 +78,10 @@ feature 'Project milestone', :feature do it 'shows the total MR and issue counts' do find('.milestone-sidebar .block', match: :first) - blocks = all('.milestone-sidebar .block') aggregate_failures 'MR and issue blocks' do - expect(blocks[3]).to have_content 1 - expect(blocks[4]).to have_content 0 + expect(find('.milestone-sidebar .block.issues')).to have_content 1 + expect(find('.milestone-sidebar .block.merge-requests')).to have_content 0 end end end diff --git a/spec/features/protected_branches/access_control_ce_spec.rb b/spec/features/protected_branches/access_control_ce_spec.rb index eb3cea775da835b69ed68992e17278ea60f4aaf3..d30e7947106cd98e7cd9f3866b52f231b1c2bc0f 100644 --- a/spec/features/protected_branches/access_control_ce_spec.rb +++ b/spec/features/protected_branches/access_control_ce_spec.rb @@ -9,7 +9,7 @@ RSpec.shared_examples "protected branches > access control > CE" do allowed_to_push_button = find(".js-allowed-to-push") unless allowed_to_push_button.text == access_type_name - allowed_to_push_button.click + allowed_to_push_button.trigger('click') within(".dropdown.open .dropdown-menu") { click_on access_type_name } end end diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index acc5641f9302e5d6bc32291be1a5d07c3184ee1e..fc9b293c393c696ccdc886e26471b87bb0bb39d1 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -8,7 +8,7 @@ feature 'Projected Branches', feature: true, js: true do before { login_as(user) } def set_protected_branch_name(branch_name) - find(".js-protected-branch-select").click + find(".js-protected-branch-select").trigger('click') find(".dropdown-input-field").set(branch_name) click_on("Create wildcard #{branch_name}") end diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb index cecb98641a6e945b05c7316825a5554b350b1be9..f32e70c2c3fdec4ad0ca497d331baa28d925033f 100644 --- a/spec/features/todos/todos_filtering_spec.rb +++ b/spec/features/todos/todos_filtering_spec.rb @@ -45,8 +45,8 @@ describe 'Dashboard > User filters todos', feature: true, js: true do wait_for_ajax - expect(find('.todos-list')).to have_content user_1.name - expect(find('.todos-list')).not_to have_content user_2.name + expect(find('.todos-list')).to have_content 'merge request' + expect(find('.todos-list')).not_to have_content 'issue' end it "shows only authors of existing todos" do diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 50c207fb9cb8312732015563a57115c31c09ee94..be5b3af417f1f3fdc7ef3f223b26efa19e5c72a1 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -99,6 +99,83 @@ describe 'Dashboard Todos', feature: true do end end + context 'User created todos for themself' do + before do + login_as(user) + end + + context 'issue assigned todo' do + before do + create(:todo, :assigned, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows issue assigned to yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You assigned issue #{issue.to_reference(full: true)} to yourself") + end + end + end + + context 'marked todo' do + before do + create(:todo, :marked, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you added a todo message' do + page.within('.js-todos-all') do + expect(page).to have_content("You added a todo for issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'mentioned todo' do + before do + create(:todo, :mentioned, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you mentioned yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'directly_addressed todo' do + before do + create(:todo, :directly_addressed, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you directly addressed yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You directly addressed yourself on issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'approval todo' do + let(:merge_request) { create(:merge_request) } + + before do + create(:todo, :approval_required, user: user, project: project, target: merge_request, author: user) + visit dashboard_todos_path + end + + it 'shows you set yourself as an approver message' do + page.within('.js-todos-all') do + expect(page).to have_content("You set yourself as an approver for merge request #{merge_request.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + end + context 'User has done todos', js: true do before do create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author) diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index a7c3c281083fa79978fec90b6eac9f6e505e84e7..c3bd0cb35425f1f432f525b995666e110d72906b 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -56,7 +56,7 @@ describe EventsHelper do it 'preserves code color scheme' do input = "```ruby\ndef test\n 'hello world'\nend\n```" - expected = '<pre class="code highlight js-syntax-highlight ruby">' \ + expected = "\n<pre class=\"code highlight js-syntax-highlight ruby\">" \ "<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \ "</code></pre>" expect(helper.event_note(input)).to eq(expected) diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index 7a84bbebd0231912f97ead4719a6ae2d9bd2e070..bc66ce83d4ae86cd7d33c18853c1851ae0ec8cb9 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' -describe Gitlab::Checks::ChangeAccess, lib: true do +describe Gitlab::Checks::ForcePush, lib: true do let(:project) { create(:project, :repository) } context "exit code checking" do it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error end it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) end end end diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb similarity index 100% rename from spec/lib/git_ref_validator_spec.rb rename to spec/lib/gitlab/git_ref_validator_spec.rb diff --git a/spec/lib/gitlab/healthchecks/db_check_spec.rb b/spec/lib/gitlab/health_checks/db_check_spec.rb similarity index 100% rename from spec/lib/gitlab/healthchecks/db_check_spec.rb rename to spec/lib/gitlab/health_checks/db_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb similarity index 100% rename from spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb rename to spec/lib/gitlab/health_checks/fs_shards_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis_check_spec.rb similarity index 100% rename from spec/lib/gitlab/healthchecks/redis_check_spec.rb rename to spec/lib/gitlab/health_checks/redis_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb similarity index 100% rename from spec/lib/gitlab/healthchecks/simple_check_shared.rb rename to spec/lib/gitlab/health_checks/simple_check_shared.rb diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb similarity index 100% rename from spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb rename to spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb similarity index 100% rename from spec/lib/gitlab/backend/shell_spec.rb rename to spec/lib/gitlab/shell_spec.rb diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb deleted file mode 100644 index 3fe8cf43934d0e988ea475515c52ce4c74a4ea4e..0000000000000000000000000000000000000000 --- a/spec/lib/light_url_builder_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'spec_helper' - -describe Gitlab::UrlBuilder, lib: true do - describe '.build' do - context 'when passing a Commit' do - it 'returns a proper URL' do - commit = build_stubbed(:commit) - - url = described_class.build(commit) - - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" - end - end - - context 'when passing an Issue' do - it 'returns a proper URL' do - issue = build_stubbed(:issue, iid: 42) - - url = described_class.build(issue) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" - end - end - - context 'when passing a MergeRequest' do - it 'returns a proper URL' do - merge_request = build_stubbed(:merge_request, iid: 42) - - url = described_class.build(merge_request) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" - end - end - - context 'when passing a Note' do - context 'on a Commit' do - it 'returns a proper URL' do - note = build_stubbed(:note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on a Commit Diff' do - it 'returns a proper URL' do - note = build_stubbed(:diff_note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on an Issue' do - it 'returns a proper URL' do - issue = create(:issue, iid: 42) - note = build_stubbed(:note_on_issue, noteable: issue) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest Diff' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a ProjectSnippet' do - it 'returns a proper URL' do - project_snippet = create(:project_snippet) - note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" - end - end - - context 'on another object' do - it 'returns a proper URL' do - project = build_stubbed(:empty_project) - - expect { described_class.build(project) }. - to raise_error(NotImplementedError, 'No URL builder defined for Project') - end - end - end - - context 'when passing a WikiPage' do - it 'returns a proper URL' do - wiki_page = build(:wiki_page) - url = described_class.build(wiki_page) - - expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" - end - end - end -end diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb index e22858d1d8fbbdcb8c229e3e0dc786aaf73856f5..2ad572bb5c75f728828ce8c439b285131bc12811 100644 --- a/spec/mailers/emails/merge_requests_spec.rb +++ b/spec/mailers/emails/merge_requests_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'email_spec' -describe Notify, "merge request notifications" do +describe Emails::MergeRequests do include EmailSpec::Matchers describe "#resolved_all_discussions_email" do diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 5ca936f28f0d0603798681c0c222d0a0f193f59b..8c1c9bf135fa58412c0dd4c9b816fbc0ba2ecb55 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'email_spec' -describe Notify do +describe Emails::Profile do include EmailSpec::Matchers include_context 'gitlab email notification' @@ -15,106 +15,104 @@ describe Notify do end end - describe 'profile notifications' do - describe 'for new users, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } - let(:token) { 'kETLwRaayvigPq_x3SNM' } + describe 'for new users, the email' do + let(:example_site_path) { root_path } + let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } + let(:token) { 'kETLwRaayvigPq_x3SNM' } - subject { Notify.new_user_email(new_user.id, token) } + subject { Notify.new_user_email(new_user.id, token) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'a new user email' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'a new user email' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'contains the password text' do - is_expected.to have_body_text /Click here to set your password/ - end + it 'contains the password text' do + is_expected.to have_body_text /Click here to set your password/ + end - it 'includes a link for user to set password' do - params = "reset_password_token=#{token}" - is_expected.to have_body_text( - %r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}} - ) - end + it 'includes a link for user to set password' do + params = "reset_password_token=#{token}" + is_expected.to have_body_text( + %r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}} + ) + end - it 'explains the reset link expiration' do - is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/) - is_expected.to have_body_text(new_user_password_url) - is_expected.to have_body_text(/\?user_email=.*%40.*/) - end + it 'explains the reset link expiration' do + is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/) + is_expected.to have_body_text(new_user_password_url) + is_expected.to have_body_text(/\?user_email=.*%40.*/) end + end - describe 'for users that signed up, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } + describe 'for users that signed up, the email' do + let(:example_site_path) { root_path } + let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } - subject { Notify.new_user_email(new_user.id) } + subject { Notify.new_user_email(new_user.id) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'a new user email' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'a new user email' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'does not contain the new user\'s password' do - is_expected.not_to have_body_text /password/ - end + it 'does not contain the new user\'s password' do + is_expected.not_to have_body_text /password/ end + end - describe 'user added ssh key' do - let(:key) { create(:personal_key) } + describe 'user added ssh key' do + let(:key) { create(:personal_key) } - subject { Notify.new_ssh_key_email(key.id) } + subject { Notify.new_ssh_key_email(key.id) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'is sent to the new user' do - is_expected.to deliver_to key.user.email - end + it 'is sent to the new user' do + is_expected.to deliver_to key.user.email + end - it 'has the correct subject' do - is_expected.to have_subject /^SSH key was added to your account$/i - end + it 'has the correct subject' do + is_expected.to have_subject /^SSH key was added to your account$/i + end - it 'contains the new ssh key title' do - is_expected.to have_body_text /#{key.title}/ - end + it 'contains the new ssh key title' do + is_expected.to have_body_text /#{key.title}/ + end - it 'includes a link to ssh keys page' do - is_expected.to have_body_text /#{profile_keys_path}/ - end + it 'includes a link to ssh keys page' do + is_expected.to have_body_text /#{profile_keys_path}/ + end - context 'with SSH key that does not exist' do - it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error } - end + context 'with SSH key that does not exist' do + it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error } end + end - describe 'user added email' do - let(:email) { create(:email) } + describe 'user added email' do + let(:email) { create(:email) } - subject { Notify.new_email_email(email.id) } + subject { Notify.new_email_email(email.id) } - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'is sent to the new user' do - is_expected.to deliver_to email.user.email - end + it 'is sent to the new user' do + is_expected.to deliver_to email.user.email + end - it 'has the correct subject' do - is_expected.to have_subject /^Email was added to your account$/i - end + it 'has the correct subject' do + is_expected.to have_subject /^Email was added to your account$/i + end - it 'contains the new email address' do - is_expected.to have_body_text /#{email.email}/ - end + it 'contains the new email address' do + is_expected.to have_body_text /#{email.email}/ + end - it 'includes a link to emails page' do - is_expected.to have_body_text /#{profile_emails_path}/ - end + it 'includes a link to emails page' do + is_expected.to have_body_text /#{profile_emails_path}/ end end end diff --git a/spec/migrations/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb similarity index 100% rename from spec/migrations/schema_spec.rb rename to spec/migrations/active_record/schema_spec.rb diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb index de791abdf3dbc66b26059b37db419a925a61922b..63ad3a3630b228b31a15512d16b5e8f07991d283 100644 --- a/spec/models/concerns/awardable_spec.rb +++ b/spec/models/concerns/awardable_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' -describe Issue, "Awardable" do +describe Awardable do let!(:issue) { create(:issue) } let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) } describe "Associations" do + subject { build(:issue) } + it { is_expected.to have_many(:award_emoji).dependent(:destroy) } end diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index 0002a00770f48537f09c4bc13a13253e2d9f62c2..8571e85627ce123e58ce63ffddca627f94d785a5 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffDiscussion, DiscussionOnDiff, model: true do +describe DiscussionOnDiff, model: true do subject { create(:diff_note_on_merge_request).to_discussion } describe "#truncated_diff_lines" do @@ -8,9 +8,9 @@ describe DiffDiscussion, DiscussionOnDiff, model: true do context "when diff is greater than allowed number of truncated diff lines " do it "returns fewer lines" do - expect(subject.diff_lines.count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES + expect(subject.diff_lines.count).to be > DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES - expect(truncated_lines.count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES + expect(truncated_lines.count).to be <= DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 4522206fab15a3077ad3c5d4207b348ff424731c..3ecba2e96870bb14ab47656a7949b5ee606e3ffa 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -1,10 +1,13 @@ require 'spec_helper' -describe Issue, "Issuable" do +describe Issuable do + let(:issuable_class) { Issue } let(:issue) { create(:issue) } let(:user) { create(:user) } describe "Associations" do + subject { build(:issue) } + it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:author) } it { is_expected.to belong_to(:assignee) } @@ -23,10 +26,14 @@ describe Issue, "Issuable" do end describe 'Included modules' do + let(:described_class) { issuable_class } + it { is_expected.to include_module(Awardable) } end describe "Validation" do + subject { build(:issue) } + before do allow(subject).to receive(:set_iid).and_return(false) end @@ -39,9 +46,11 @@ describe Issue, "Issuable" do end describe "Scope" do - it { expect(described_class).to respond_to(:opened) } - it { expect(described_class).to respond_to(:closed) } - it { expect(described_class).to respond_to(:assigned) } + subject { build(:issue) } + + it { expect(issuable_class).to respond_to(:opened) } + it { expect(issuable_class).to respond_to(:closed) } + it { expect(issuable_class).to respond_to(:assigned) } end describe 'author_name' do @@ -115,16 +124,16 @@ describe Issue, "Issuable" do let!(:searchable_issue) { create(:issue, title: "Searchable issue") } it 'returns notes with a matching title' do - expect(described_class.search(searchable_issue.title)). + expect(issuable_class.search(searchable_issue.title)). to eq([searchable_issue]) end it 'returns notes with a partially matching title' do - expect(described_class.search('able')).to eq([searchable_issue]) + expect(issuable_class.search('able')).to eq([searchable_issue]) end it 'returns notes with a matching title regardless of the casing' do - expect(described_class.search(searchable_issue.title.upcase)). + expect(issuable_class.search(searchable_issue.title.upcase)). to eq([searchable_issue]) end end @@ -135,31 +144,31 @@ describe Issue, "Issuable" do end it 'returns notes with a matching title' do - expect(described_class.full_search(searchable_issue.title)). + expect(issuable_class.full_search(searchable_issue.title)). to eq([searchable_issue]) end it 'returns notes with a partially matching title' do - expect(described_class.full_search('able')).to eq([searchable_issue]) + expect(issuable_class.full_search('able')).to eq([searchable_issue]) end it 'returns notes with a matching title regardless of the casing' do - expect(described_class.full_search(searchable_issue.title.upcase)). + expect(issuable_class.full_search(searchable_issue.title.upcase)). to eq([searchable_issue]) end it 'returns notes with a matching description' do - expect(described_class.full_search(searchable_issue.description)). + expect(issuable_class.full_search(searchable_issue.description)). to eq([searchable_issue]) end it 'returns notes with a partially matching description' do - expect(described_class.full_search(searchable_issue.description)). + expect(issuable_class.full_search(searchable_issue.description)). to eq([searchable_issue]) end it 'returns notes with a matching description regardless of the casing' do - expect(described_class.full_search(searchable_issue.description.upcase)). + expect(issuable_class.full_search(searchable_issue.description.upcase)). to eq([searchable_issue]) end end diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index 92cc8859a8cc74b64e75bbb052bf430eceafaa16..bdae742ff1def07b363159efbac8798369d9eb48 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequest, Noteable, model: true do +describe Noteable, model: true do let!(:active_diff_note1) { create(:diff_note_on_merge_request) } let(:project) { active_diff_note1.project } subject { active_diff_note1.noteable } diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb index 255b584a85e78ec4eb039e0c3c21b14c0365384f..494e6f1b6f65631794f3e3ba4331ee458276cac1 100644 --- a/spec/models/concerns/relative_positioning_spec.rb +++ b/spec/models/concerns/relative_positioning_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issue, 'RelativePositioning' do +describe RelativePositioning do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:issue1) { create(:issue, project: project) } diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb index fd3b830757148c01bc61563dc075b301d929af0d..e698207166c220289047c8f3ea245a5eb52b503d 100644 --- a/spec/models/concerns/spammable_spec.rb +++ b/spec/models/concerns/spammable_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' -describe Issue, 'Spammable' do +describe Spammable do let(:issue) { create(:issue, description: 'Test Desc.') } describe 'Associations' do + subject { build(:issue) } + it { is_expected.to have_one(:user_agent_detail).dependent(:destroy) } end diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb index c3af7a0960ffb28d42fe2b8e64992efe520c6182..8c945686b6663a95c0202d6490a63c34b08b26a9 100644 --- a/spec/models/concerns/strip_attribute_spec.rb +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestone, "StripAttribute" do +describe StripAttribute do let(:milestone) { create(:milestone) } describe ".strip_attributes" do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index b0f3657d3b5fa769e63063df1ebf736ec4902739..ccc3deac199b0b17601a96ea8d861674cc37f3a6 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -390,13 +390,15 @@ describe Member, models: true do %w[project group].each do |source_type| context "when source is a #{source_type}" do let!(:source) { create(source_type, :public, :access_requestable) } - let!(:user) { create(:user) } let!(:admin) { create(:admin) } + let(:user1) { create(:user) } + let(:user2) { create(:user) } it 'returns a <Source>Member objects' do - members = described_class.add_users(source, [user], :master) + members = described_class.add_users(source, [user1, user2], :master) expect(members).to be_a Array + expect(members.size).to eq(2) expect(members.first).to be_a "#{source_type.classify}Member".constantize expect(members.first).to be_persisted end diff --git a/spec/models/project_services/pipeline_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb similarity index 100% rename from spec/models/project_services/pipeline_email_service_spec.rb rename to spec/models/project_services/pipelines_email_service_spec.rb diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index 581305ad39f06e6f03b1a4382764ec42d990c1ee..3f80e1ac5343faece7081c5ec4405f2ca16d92d4 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -125,4 +125,50 @@ describe Todo, models: true do expect(subject.target_reference).to eq issue.to_reference(full: true) end end + + describe '#self_added?' do + let(:user_1) { build(:user) } + + before do + subject.user = user_1 + end + + it 'is true when the user is the author' do + subject.author = user_1 + + expect(subject).to be_self_added + end + + it 'is false when the user is not the author' do + subject.author = build(:user) + + expect(subject).not_to be_self_added + end + end + + describe '#self_assigned?' do + let(:user_1) { build(:user) } + + before do + subject.user = user_1 + subject.author = user_1 + subject.action = Todo::ASSIGNED + end + + it 'is true when todo is ASSIGNED and self_added' do + expect(subject).to be_self_assigned + end + + it 'is false when the todo is not ASSIGNED' do + subject.action = Todo::MENTIONED + + expect(subject).not_to be_self_assigned + end + + it 'is false when todo is not self_added' do + subject.author = build(:user) + + expect(subject).not_to be_self_assigned + end + end end diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index 2905d5b26a5db0edf382ed0c3873b07d824e803a..9a870b7fda12f3e5a7f794941beeabfa2fc9f521 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -1,118 +1,192 @@ require 'spec_helper' describe IssuePolicy, models: true do - let(:user) { create(:user) } - - describe '#rules' do - context 'using a regular issue' do - let(:project) { create(:empty_project, :public) } - let(:issue) { create(:issue, project: project) } - let(:policies) { described_class.abilities(user, issue).to_set } - - context 'with a regular user' do - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end - - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end - - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end - end + let(:guest) { create(:user) } + let(:author) { create(:user) } + let(:assignee) { create(:user) } + let(:reporter) { create(:user) } + let(:group) { create(:group, :public) } + let(:reporter_from_group_link) { create(:user) } + + def permissions(user, issue) + described_class.abilities(user, issue).to_set + end + + context 'a private project' do + let(:non_member) { create(:user) } + let(:project) { create(:empty_project, :private) } + let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } + let(:issue_no_assignee) { create(:issue, project: project) } + + before do + project.team << [guest, :guest] + project.team << [author, :guest] + project.team << [assignee, :guest] + project.team << [reporter, :reporter] + + group.add_reporter(reporter_from_group_link) + + create(:project_group_link, group: group, project: project) + end + + it 'does not allow non-members to read issues' do + expect(permissions(non_member, issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows guests to read issues' do + expect(permissions(guest, issue)).to include(:read_issue) + expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + + expect(permissions(guest, issue_no_assignee)).to include(:read_issue) + expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows reporters to read, update, and admin issues' do + expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporters from group links to read, update, and admin issues' do + expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows issue authors to read and update their issues' do + expect(permissions(author, issue)).to include(:read_issue, :update_issue) + expect(permissions(author, issue)).not_to include(:admin_issue) + + expect(permissions(author, issue_no_assignee)).to include(:read_issue) + expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows issue assignees to read and update their issues' do + expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, issue)).not_to include(:admin_issue) + + expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) + expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end - context 'with a user that is a project reporter' do - before do - project.team << [user, :reporter] - end + context 'with confidential issues' do + let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } + let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + it 'does not allow non-members to read confidential issues' do + expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end + + it 'does not allow guests to read confidential issues' do + expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'includes the admin_issue permission' do - expect(policies).to include(:admin_issue) - end + it 'allows reporters to read, update, and admin confidential issues' do + expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - it 'includes the update_issue permission' do - expect(policies).to include(:update_issue) - end + it 'allows reporters from group links to read, update, and admin confidential issues' do + expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) end - context 'with a user that is a project guest' do - before do - project.team << [user, :guest] - end + it 'allows issue authors to read and update their confidential issues' do + expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + it 'allows issue assignees to read and update their confidential issues' do + expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end + expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end end + end - context 'using a confidential issue' do - let(:issue) { create(:issue, :confidential) } + context 'a public project' do + let(:project) { create(:empty_project, :public) } + let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } + let(:issue_no_assignee) { create(:issue, project: project) } - context 'with a regular user' do - let(:policies) { described_class.abilities(user, issue).to_set } + before do + project.team << [guest, :guest] + project.team << [reporter, :reporter] - it 'does not include the read_issue permission' do - expect(policies).not_to include(:read_issue) - end + group.add_reporter(reporter_from_group_link) - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + create(:project_group_link, group: group, project: project) + end - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end - end + it 'allows guests to read issues' do + expect(permissions(guest, issue)).to include(:read_issue) + expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + + expect(permissions(guest, issue_no_assignee)).to include(:read_issue) + expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows reporters to read, update, and admin issues' do + expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporters from group links to read, update, and admin issues' do + expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - context 'with a user that is a project member' do - let(:policies) { described_class.abilities(user, issue).to_set } + it 'allows issue authors to read and update their issues' do + expect(permissions(author, issue)).to include(:read_issue, :update_issue) + expect(permissions(author, issue)).not_to include(:admin_issue) - before do - issue.project.team << [user, :reporter] - end + expect(permissions(author, issue_no_assignee)).to include(:read_issue) + expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows issue assignees to read and update their issues' do + expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, issue)).not_to include(:admin_issue) - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) + expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end - it 'includes the admin_issue permission' do - expect(policies).to include(:admin_issue) - end + context 'with confidential issues' do + let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } + let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - it 'includes the update_issue permission' do - expect(policies).to include(:update_issue) - end + it 'does not allow guests to read confidential issues' do + expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end - context 'without a user' do - let(:policies) { described_class.abilities(nil, issue).to_set } + it 'allows reporters to read, update, and admin confidential issues' do + expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporter from group links to read, update, and admin confidential issues' do + expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the read_issue permission' do - expect(policies).not_to include(:read_issue) - end + it 'allows issue authors to read and update their confidential issues' do + expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).not_to include(:admin_issue) + + expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + it 'allows issue assignees to read and update their confidential issues' do + expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end + expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end end end diff --git a/spec/policies/issues_policy_spec.rb b/spec/policies/issues_policy_spec.rb deleted file mode 100644 index 2b7b6cad6547c70d0cdef934679debfc9470de72..0000000000000000000000000000000000000000 --- a/spec/policies/issues_policy_spec.rb +++ /dev/null @@ -1,193 +0,0 @@ -require 'spec_helper' - -describe IssuePolicy, models: true do - let(:guest) { create(:user) } - let(:author) { create(:user) } - let(:assignee) { create(:user) } - let(:reporter) { create(:user) } - let(:group) { create(:group, :public) } - let(:reporter_from_group_link) { create(:user) } - - def permissions(user, issue) - IssuePolicy.abilities(user, issue).to_set - end - - context 'a private project' do - let(:non_member) { create(:user) } - let(:project) { create(:empty_project, :private) } - let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } - let(:issue_no_assignee) { create(:issue, project: project) } - - before do - project.team << [guest, :guest] - project.team << [author, :guest] - project.team << [assignee, :guest] - project.team << [reporter, :reporter] - - group.add_reporter(reporter_from_group_link) - - create(:project_group_link, group: group, project: project) - end - - it 'does not allow non-members to read issues' do - expect(permissions(non_member, issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) - - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) - - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) - - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - context 'with confidential issues' do - let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } - let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - - it 'does not allow non-members to read confidential issues' do - expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - end - end - - context 'a public project' do - let(:project) { create(:empty_project, :public) } - let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } - let(:issue_no_assignee) { create(:issue, project: project) } - - before do - project.team << [guest, :guest] - project.team << [reporter, :reporter] - - group.add_reporter(reporter_from_group_link) - - create(:project_group_link, group: group, project: project) - end - - it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) - - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) - - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) - - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - context 'with confidential issues' do - let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } - let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - - it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporter from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - end - end -end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index b5897b2e34618aee00d6589aaa7583c64677c8e5..868fef65c1ca2b1a1387426e6f80fb85bd2eca47 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::API do +describe 'doorkeeper access' do let!(:user) { create(:user) } let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) } let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" } diff --git a/spec/requests/api/api_internal_helpers_spec.rb b/spec/requests/api/helpers/internal_helpers_spec.rb similarity index 100% rename from spec/requests/api/api_internal_helpers_spec.rb rename to spec/requests/api/helpers/internal_helpers_spec.rb diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb index 819df1059600158e23714e6d48bc71d8f8f34ace..0d56e1f732ed6f9bdd7bf8c96d6695eac02d4c16 100644 --- a/spec/requests/api/oauth_tokens_spec.rb +++ b/spec/requests/api/oauth_tokens_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::API do +describe 'OAuth tokens' do context 'Resource Owner Password Credentials' do def request_oauth_token(user) post '/oauth/token', username: user.username, password: user.password, grant_type: 'password' diff --git a/spec/routing/environments_spec.rb b/spec/routing/environments_spec.rb index ba124de70bb195fcbdee6b9974d778c1e7732b0e..624f3c43f0a8cf9ba5a9b348aa1b2a7e2e198f58 100644 --- a/spec/routing/environments_spec.rb +++ b/spec/routing/environments_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::EnvironmentsController, :routing do +describe 'environments routing', :routing do let(:project) { create(:empty_project) } let(:environment) do diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb index 24592942a967e77b20d38dc6391550af2890239d..54ed87b5520b1c58159d336c5d7c6267c06ff866 100644 --- a/spec/routing/notifications_routing_spec.rb +++ b/spec/routing/notifications_routing_spec.rb @@ -1,13 +1,11 @@ require "spec_helper" -describe Profiles::NotificationsController do - describe "routing" do - it "routes to #show" do - expect(get("/profile/notifications")).to route_to("profiles/notifications#show") - end +describe "notifications routing" do + it "routes to #show" do + expect(get("/profile/notifications")).to route_to("profiles/notifications#show") + end - it "routes to #update" do - expect(put("/profile/notifications")).to route_to("profiles/notifications#update") - end + it "routes to #update" do + expect(put("/profile/notifications")).to route_to("profiles/notifications#update") end end diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_issue_entity_spec.rb similarity index 100% rename from spec/serializers/analytics_generic_entity_spec.rb rename to spec/serializers/analytics_issue_entity_spec.rb diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 4a4929daefc9638b219d21d3c08e5b12ffa32257..c3b4c2176eeba2934fe7f94018ae328100051eee 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -1,15 +1,15 @@ require 'spec_helper.rb' -class DummyService < Issues::BaseService - include ::Issues::ResolveDiscussions +describe Issues::ResolveDiscussions, services: true do + class DummyService < Issues::BaseService + include ::Issues::ResolveDiscussions - def initialize(*args) - super - filter_resolve_discussion_params + def initialize(*args) + super + filter_resolve_discussion_params + end end -end -describe DummyService, services: true do let(:project) { create(:project, :repository) } let(:user) { create(:user) } @@ -23,7 +23,7 @@ describe DummyService, services: true do let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "other") } describe "#merge_request_for_resolving_discussion" do - let(:service) { described_class.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid) } + let(:service) { DummyService.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid) } it "finds the merge request" do expect(service.merge_request_to_resolve_discussions_of).to eq(merge_request) @@ -43,7 +43,7 @@ describe DummyService, services: true do describe "#discussions_to_resolve" do it "contains a single discussion when matching merge request and discussion are passed" do - service = described_class.new( + service = DummyService.new( project, user, discussion_to_resolve: discussion.id, @@ -61,7 +61,7 @@ describe DummyService, services: true do noteable: merge_request, project: merge_request.target_project, line_number: 15)]) - service = described_class.new( + service = DummyService.new( project, user, merge_request_to_resolve_discussions_of: merge_request.iid @@ -79,7 +79,7 @@ describe DummyService, services: true do project: merge_request.target_project, line_number: 15, )]) - service = described_class.new( + service = DummyService.new( project, user, merge_request_to_resolve_discussions_of: merge_request.iid @@ -92,7 +92,7 @@ describe DummyService, services: true do end it "is empty when a discussion and another merge request are passed" do - service = described_class.new( + service = DummyService.new( project, user, discussion_to_resolve: discussion.id, diff --git a/spec/services/merge_requests/resolved_discussion_notification_service.rb b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb similarity index 100% rename from spec/services/merge_requests/resolved_discussion_notification_service.rb rename to spec/services/merge_requests/resolved_discussion_notification_service_spec.rb diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb index a63281f0eab7c8057e9985806dd8f7188bb59729..29e65fe7ce6da861c4c34b57e8e5cc54b7cd3039 100644 --- a/spec/services/slash_commands/interpret_service_spec.rb +++ b/spec/services/slash_commands/interpret_service_spec.rb @@ -52,7 +52,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'unassign command' do it 'populates assignee_id: nil if content contains /unassign' do - issuable.update(assignee_id: developer.id) + issuable.update!(assignee_id: developer.id) _, updates = service.execute(content, issuable) expect(updates).to eq(assignee_id: nil) @@ -70,7 +70,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'remove_milestone command' do it 'populates milestone_id: nil if content contains /remove_milestone' do - issuable.update(milestone_id: milestone.id) + issuable.update!(milestone_id: milestone.id) _, updates = service.execute(content, issuable) expect(updates).to eq(milestone_id: nil) @@ -108,7 +108,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'unlabel command' do it 'fetches label ids and populates remove_label_ids if content contains /unlabel' do - issuable.update(label_ids: [inprogress.id]) # populate the label + issuable.update!(label_ids: [inprogress.id]) # populate the label _, updates = service.execute(content, issuable) expect(updates).to eq(remove_label_ids: [inprogress.id]) @@ -117,7 +117,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'multiple unlabel command' do it 'fetches label ids and populates remove_label_ids if content contains mutiple /unlabel' do - issuable.update(label_ids: [inprogress.id, bug.id]) # populate the label + issuable.update!(label_ids: [inprogress.id, bug.id]) # populate the label _, updates = service.execute(content, issuable) expect(updates).to eq(remove_label_ids: [inprogress.id, bug.id]) @@ -126,7 +126,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'unlabel command with no argument' do it 'populates label_ids: [] if content contains /unlabel with no arguments' do - issuable.update(label_ids: [inprogress.id]) # populate the label + issuable.update!(label_ids: [inprogress.id]) # populate the label _, updates = service.execute(content, issuable) expect(updates).to eq(label_ids: []) @@ -135,7 +135,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'relabel command' do it 'populates label_ids: [] if content contains /relabel' do - issuable.update(label_ids: [bug.id]) # populate the label + issuable.update!(label_ids: [bug.id]) # populate the label inprogress # populate the label _, updates = service.execute(content, issuable) @@ -187,7 +187,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'remove_due_date command' do it 'populates due_date: nil if content contains /remove_due_date' do - issuable.update(due_date: Date.today) + issuable.update!(due_date: Date.today) _, updates = service.execute(content, issuable) expect(updates).to eq(due_date: nil) @@ -204,7 +204,7 @@ describe SlashCommands::InterpretService, services: true do shared_examples 'unwip command' do it 'returns wip_event: "unwip" if content contains /wip' do - issuable.update(title: issuable.wip_title) + issuable.update!(title: issuable.wip_title) _, updates = service.execute(content, issuable) expect(updates).to eq(wip_event: 'unwip') @@ -727,5 +727,75 @@ describe SlashCommands::InterpretService, services: true do end end end + + context '/board_move command' do + let(:todo) { create(:label, project: project, title: 'To Do') } + let(:inreview) { create(:label, project: project, title: 'In Review') } + let(:content) { %{/board_move ~"#{inreview.title}"} } + + let!(:board) { create(:board, project: project) } + let!(:todo_list) { create(:list, board: board, label: todo) } + let!(:inreview_list) { create(:list, board: board, label: inreview) } + let!(:inprogress_list) { create(:list, board: board, label: inprogress) } + + it 'populates remove_label_ids for all current board columns' do + issue.update!(label_ids: [todo.id, inprogress.id]) + + _, updates = service.execute(content, issue) + + expect(updates[:remove_label_ids]).to match_array([todo.id, inprogress.id]) + end + + it 'populates add_label_ids with the id of the given label' do + _, updates = service.execute(content, issue) + + expect(updates[:add_label_ids]).to eq([inreview.id]) + end + + it 'does not include the given label id in remove_label_ids' do + issue.update!(label_ids: [todo.id, inreview.id]) + + _, updates = service.execute(content, issue) + + expect(updates[:remove_label_ids]).to match_array([todo.id]) + end + + it 'does not remove label ids that are not lists on the board' do + issue.update!(label_ids: [todo.id, bug.id]) + + _, updates = service.execute(content, issue) + + expect(updates[:remove_label_ids]).to match_array([todo.id]) + end + + context 'if the project has multiple boards' do + let(:issuable) { issue } + before { create(:board, project: project) } + it_behaves_like 'empty command' + end + + context 'if the given label does not exist' do + let(:issuable) { issue } + let(:content) { '/board_move ~"Fake Label"' } + it_behaves_like 'empty command' + end + + context 'if multiple labels are given' do + let(:issuable) { issue } + let(:content) { %{/board_move ~"#{inreview.title}" ~"#{todo.title}"} } + it_behaves_like 'empty command' + end + + context 'if the given label is not a list on the board' do + let(:issuable) { issue } + let(:content) { %{/board_move ~"#{bug.title}"} } + it_behaves_like 'empty command' + end + + context 'if issuable is not an Issue' do + let(:issuable) { merge_request } + it_behaves_like 'empty command' + end + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e67ad8f345593e9bd433cd475db6b9324d58884a..e2d5928e5b2420962dd101421bf17ce4a9d4356a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,8 +13,9 @@ rspec_profiling_is_configured = ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING'] branch_can_be_profiled = - ENV['CI_COMMIT_REF_NAME'] == 'master' || - ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/ + ENV['GITLAB_DATABASE'] == 'postgresql' && + (ENV['CI_COMMIT_REF_NAME'] == 'master' || + ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/) if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled) require 'rspec_profiling/rspec' diff --git a/spec/unicorn/unicorn_spec.rb b/spec/unicorn/unicorn_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8518c047a470e573aafad5f185d3eaa931bd444e --- /dev/null +++ b/spec/unicorn/unicorn_spec.rb @@ -0,0 +1,98 @@ +require 'fileutils' + +require 'excon' + +require 'spec_helper' + +describe 'Unicorn' do + before(:all) do + config_lines = File.read('config/unicorn.rb.example').split("\n") + + # Remove these because they make setup harder. + config_lines = config_lines.reject do |line| + %w[ + working_directory + worker_processes + listen + pid + stderr_path + stdout_path + ].any? { |prefix| line.start_with?(prefix) } + end + + config_lines << "working_directory '#{Rails.root}'" + + # We want to have exactly 1 worker process because that makes it + # predictable which process will handle our requests. + config_lines << 'worker_processes 1' + + @socket_path = File.join(Dir.pwd, 'tmp/tests/unicorn.socket') + config_lines << "listen '#{@socket_path}'" + + ready_file = 'tmp/tests/unicorn-worker-ready' + FileUtils.rm_f(ready_file) + after_fork_index = config_lines.index { |l| l.start_with?('after_fork') } + config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)") + + config_path = 'tmp/tests/unicorn.rb' + File.write(config_path, config_lines.join("\n") + "\n") + + cmd = %W[unicorn -E test -c #{config_path} #{Rails.root.join('config.ru')}] + @unicorn_master_pid = spawn(*cmd) + wait_unicorn_boot!(@unicorn_master_pid, ready_file) + WebMock.allow_net_connect! + end + + %w[SIGQUIT SIGTERM SIGKILL].each do |signal| + it "has a worker that self-terminates on signal #{signal}" do + response = Excon.get('unix:///unicorn_test/pid', socket: @socket_path) + expect(response.status).to eq(200) + + worker_pid = response.body.to_i + expect(worker_pid).to be > 0 + + begin + Excon.post('unix:///unicorn_test/kill', socket: @socket_path, body: "signal=#{signal}") + rescue Excon::Error::Socket + # The connection may be closed abruptly + end + + expect(pid_gone?(worker_pid)).to eq(true) + end + end + + after(:all) do + WebMock.disable_net_connect!(allow_localhost: true) + Process.kill('TERM', @unicorn_master_pid) + end + + def wait_unicorn_boot!(master_pid, ready_file) + # Unicorn should boot in under 60 seconds so 120 seconds seems like a good timeout. + timeout = 120 + timeout.times do + return if File.exist?(ready_file) + pid = Process.waitpid(master_pid, Process::WNOHANG) + raise "unicorn failed to boot: #{$?}" unless pid.nil? + + sleep 1 + end + + raise "unicorn boot timed out after #{timeout} seconds" + end + + def pid_gone?(pid) + # Worker termination should take less than a second. That makes 10 + # seconds a generous timeout. + 10.times do + begin + Process.kill(0, pid) + rescue Errno::ESRCH + return true + end + + sleep 1 + end + + false + end +end diff --git a/spec/workers/pipeline_proccess_worker_spec.rb b/spec/workers/pipeline_process_worker_spec.rb similarity index 100% rename from spec/workers/pipeline_proccess_worker_spec.rb rename to spec/workers/pipeline_process_worker_spec.rb