Commit 759f102d authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett

Merge branch 'master' into balsalmiq-support

parents adff7923 03f13af5
...@@ -19,8 +19,8 @@ variables: ...@@ -19,8 +19,8 @@ variables:
before_script: before_script:
- bundle --version - bundle --version
- . scripts/utils.sh - source scripts/utils.sh
- ./scripts/prepare_build.sh - source scripts/prepare_build.sh
stages: stages:
- prepare - prepare
...@@ -68,6 +68,13 @@ stages: ...@@ -68,6 +68,13 @@ stages:
- //@gitlab-org/gitlab-ee - //@gitlab-org/gitlab-ee
- //@gitlab/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 .rspec-knapsack: &rspec-knapsack
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
...@@ -91,11 +98,13 @@ stages: ...@@ -91,11 +98,13 @@ stages:
.rspec-knapsack-pg: &rspec-knapsack-pg .rspec-knapsack-pg: &rspec-knapsack-pg
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *use-pg <<: *use-pg
<<: *except-docs
.rspec-knapsack-mysql: &rspec-knapsack-mysql .rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-master-and-ee-or-mysql
<<: *except-docs
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
...@@ -120,16 +129,19 @@ stages: ...@@ -120,16 +129,19 @@ stages:
.spinach-knapsack-pg: &spinach-knapsack-pg .spinach-knapsack-pg: &spinach-knapsack-pg
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *use-pg <<: *use-pg
<<: *except-docs
.spinach-knapsack-mysql: &spinach-knapsack-mysql .spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-master-and-ee-or-mysql
<<: *except-docs
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
knapsack: knapsack:
<<: *knapsack-state <<: *knapsack-state
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: prepare stage: prepare
script: script:
- mkdir -p knapsack/${CI_PROJECT_NAME}/ - mkdir -p knapsack/${CI_PROJECT_NAME}/
...@@ -156,6 +168,7 @@ update-knapsack: ...@@ -156,6 +168,7 @@ update-knapsack:
setup-test-env: setup-test-env:
<<: *use-pg <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: prepare stage: prepare
script: script:
- node --version - node --version
...@@ -243,6 +256,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql ...@@ -243,6 +256,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql
.exec: &exec .exec: &exec
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: test stage: test
script: script:
- bundle exec $CI_JOB_NAME - bundle exec $CI_JOB_NAME
...@@ -250,6 +264,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql ...@@ -250,6 +264,7 @@ spinach mysql 9 10: *spinach-knapsack-mysql
rubocop: rubocop:
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: test stage: test
script: script:
- bundle exec "rubocop --require rubocop-rspec" - bundle exec "rubocop --require rubocop-rspec"
...@@ -266,6 +281,7 @@ rake downtime_check: ...@@ -266,6 +281,7 @@ rake downtime_check:
- master - master
- tags - tags
- /^[\d-]+-stable(-ee)?$/ - /^[\d-]+-stable(-ee)?$/
- /^docs\/*/
rake ee_compat_check: rake ee_compat_check:
<<: *exec <<: *exec
...@@ -296,10 +312,12 @@ rake ee_compat_check: ...@@ -296,10 +312,12 @@ rake ee_compat_check:
rake pg db:migrate:reset: rake pg db:migrate:reset:
<<: *db-migrate-reset <<: *db-migrate-reset
<<: *use-pg <<: *use-pg
<<: *except-docs
rake mysql db:migrate:reset: rake mysql db:migrate:reset:
<<: *db-migrate-reset <<: *db-migrate-reset
<<: *use-mysql <<: *use-mysql
<<: *except-docs
.db-rollback: &db-rollback .db-rollback: &db-rollback
stage: test stage: test
...@@ -311,10 +329,12 @@ rake mysql db:migrate:reset: ...@@ -311,10 +329,12 @@ rake mysql db:migrate:reset:
rake pg db:rollback: rake pg db:rollback:
<<: *db-rollback <<: *db-rollback
<<: *use-pg <<: *use-pg
<<: *except-docs
rake mysql db:rollback: rake mysql db:rollback:
<<: *db-rollback <<: *db-rollback
<<: *use-mysql <<: *use-mysql
<<: *except-docs
.db-seed_fu: &db-seed_fu .db-seed_fu: &db-seed_fu
stage: test stage: test
...@@ -336,14 +356,17 @@ rake mysql db:rollback: ...@@ -336,14 +356,17 @@ rake mysql db:rollback:
rake pg db:seed_fu: rake pg db:seed_fu:
<<: *db-seed_fu <<: *db-seed_fu
<<: *use-pg <<: *use-pg
<<: *except-docs
rake mysql db:seed_fu: rake mysql db:seed_fu:
<<: *db-seed_fu <<: *db-seed_fu
<<: *use-mysql <<: *use-mysql
<<: *except-docs
rake gitlab:assets:compile: rake gitlab:assets:compile:
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
dependencies: [] dependencies: []
variables: variables:
NODE_ENV: "production" NODE_ENV: "production"
...@@ -367,6 +390,7 @@ rake karma: ...@@ -367,6 +390,7 @@ rake karma:
stage: test stage: test
<<: *use-pg <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
script: script:
...@@ -447,6 +471,7 @@ coverage: ...@@ -447,6 +471,7 @@ coverage:
stage: post-test stage: post-test
services: [] services: []
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
...@@ -462,6 +487,7 @@ coverage: ...@@ -462,6 +487,7 @@ coverage:
lint:javascript: lint:javascript:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: test stage: test
before_script: [] before_script: []
script: script:
...@@ -469,6 +495,7 @@ lint:javascript: ...@@ -469,6 +495,7 @@ lint:javascript:
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs
stage: post-test stage: post-test
before_script: [] before_script: []
script: script:
......
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 ### Summary
(Summarize the bug encountered concisely) (Summarize the bug encountered concisely)
...@@ -56,3 +70,5 @@ logs, and code as it's very hard to read otherwise.) ...@@ -56,3 +70,5 @@ logs, and code as it's very hard to read otherwise.)
### Possible fixes ### Possible fixes
(If you can, link to the line of code that might be responsible for the problem) (If you can, link to the line of code that might be responsible for the problem)
/label ~bug
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 ### Description
(Include problem, use cases, benefits, and/or goals) (Include problem, use cases, benefits, and/or goals)
...@@ -15,3 +28,5 @@ ...@@ -15,3 +28,5 @@
3. How does someone use this 3. How does someone use this
During implementation, this can then be copied and used as a starter for the documentation.) During implementation, this can then be copied and used as a starter for the documentation.)
/label ~"feature proposal"
...@@ -983,10 +983,12 @@ RSpec/ExpectActual: ...@@ -983,10 +983,12 @@ RSpec/ExpectActual:
# Checks the file and folder naming of the spec file. # Checks the file and folder naming of the spec file.
RSpec/FilePath: RSpec/FilePath:
Enabled: false Enabled: true
CustomTransform: IgnoreMethods: true
RuboCop: rubocop Exclude:
RSpec: rspec - 'qa/**/*'
- 'spec/javascripts/fixtures/*'
- 'spec/requests/api/v3/*'
# Checks if there are focused specs. # Checks if there are focused specs.
RSpec/Focus: RSpec/Focus:
......
...@@ -330,7 +330,7 @@ GEM ...@@ -330,7 +330,7 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grpc (1.1.2) grpc (1.2.5)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
haml (4.0.7) haml (4.0.7)
......
...@@ -227,8 +227,8 @@ ...@@ -227,8 +227,8 @@
.award-control-icon-positive, .award-control-icon-positive,
.award-control-icon-super-positive { .award-control-icon-super-positive {
position: absolute; position: absolute;
left: 7px; left: 11px;
bottom: 9px; bottom: 7px;
opacity: 0; opacity: 0;
@include transition(opacity, transform); @include transition(opacity, transform);
} }
......
...@@ -61,11 +61,13 @@ ...@@ -61,11 +61,13 @@
.file-content { .file-content {
background: $white-light; background: $white-light;
&.image_file { &.image_file,
&.video {
background: $file-image-bg; background: $file-image-bg;
text-align: center; text-align: center;
img { img,
video {
padding: 20px; padding: 20px;
max-width: 80%; max-width: 80%;
} }
......
...@@ -38,6 +38,7 @@ module ServiceParams ...@@ -38,6 +38,7 @@ module ServiceParams
:new_issue_url, :new_issue_url,
:notify, :notify,
:notify_only_broken_pipelines, :notify_only_broken_pipelines,
:notify_only_default_branch,
:password, :password,
:priority, :priority,
:project_key, :project_key,
......
...@@ -5,7 +5,7 @@ module Projects ...@@ -5,7 +5,7 @@ module Projects
before_action :authorize_admin_project! before_action :authorize_admin_project!
layout "project_settings" layout "project_settings"
def show def show
@hooks = @project.hooks @hooks = @project.hooks
@hook = ProjectHook.new @hook = ProjectHook.new
......
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
...@@ -10,11 +10,12 @@ module EventsHelper ...@@ -10,11 +10,12 @@ module EventsHelper
'deleted' => 'icon_trash_o' 'deleted' => 'icon_trash_o'
}.freeze }.freeze
def link_to_author(event) def link_to_author(event, self_added: false)
author = event.author author = event.author
if 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 else
event.author_name event.author_name
end end
......
...@@ -74,7 +74,7 @@ module MarkupHelper ...@@ -74,7 +74,7 @@ module MarkupHelper
context[:project] ||= @project context[:project] ||= @project
html = markdown_unsafe(text, context) html = markdown_unsafe(text, context)
banzai_postprocess(html, context) prepare_for_rendering(html, context)
end end
def markdown_field(object, field) def markdown_field(object, field)
...@@ -82,13 +82,13 @@ module MarkupHelper ...@@ -82,13 +82,13 @@ module MarkupHelper
return '' unless object.present? return '' unless object.present?
html = Banzai.render_field(object, field) html = Banzai.render_field(object, field)
banzai_postprocess(html, object.banzai_render_context(field)) prepare_for_rendering(html, object.banzai_render_context(field))
end end
def markup(file_name, text, context = {}) def markup(file_name, text, context = {})
context[:project] ||= @project context[:project] ||= @project
html = context.delete(:rendered) || markup_unsafe(file_name, text, context) html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
banzai_postprocess(html, context) prepare_for_rendering(html, context)
end end
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
...@@ -107,14 +107,14 @@ module MarkupHelper ...@@ -107,14 +107,14 @@ module MarkupHelper
wiki_page.formatted_content.html_safe wiki_page.formatted_content.html_safe
end end
banzai_postprocess(html, context) prepare_for_rendering(html, context)
end end
def markup_unsafe(file_name, text, context = {}) def markup_unsafe(file_name, text, context = {})
return '' unless text.present? return '' unless text.present?
if gitlab_markdown?(file_name) if gitlab_markdown?(file_name)
Hamlit::RailsHelpers.preserve(markdown_unsafe(text, context)) markdown_unsafe(text, context)
elsif asciidoc?(file_name) elsif asciidoc?(file_name)
asciidoc_unsafe(text) asciidoc_unsafe(text)
elsif plain?(file_name) elsif plain?(file_name)
...@@ -225,8 +225,7 @@ module MarkupHelper ...@@ -225,8 +225,7 @@ module MarkupHelper
Gitlab::OtherMarkup.render(file_name, text) Gitlab::OtherMarkup.render(file_name, text)
end end
# Calls Banzai.post_process with some common context options def prepare_for_rendering(html, context = {})
def banzai_postprocess(html, context = {})
return '' unless html.present? return '' unless html.present?
context.merge!( context.merge!(
...@@ -239,7 +238,9 @@ module MarkupHelper ...@@ -239,7 +238,9 @@ module MarkupHelper
requested_path: @path requested_path: @path
) )
Banzai.post_process(html, context) html = Banzai.post_process(html, context)
Hamlit::RailsHelpers.preserve(html)
end end
extend self extend self
......
...@@ -13,13 +13,13 @@ module TodosHelper ...@@ -13,13 +13,13 @@ module TodosHelper
def todo_action_name(todo) def todo_action_name(todo)
case todo.action case todo.action
when Todo::ASSIGNED then 'assigned you' when Todo::ASSIGNED then todo.self_added? ? 'assigned' : 'assigned you'
when Todo::MENTIONED then 'mentioned you on' when Todo::MENTIONED then "mentioned #{todo_action_subject(todo)} on"
when Todo::BUILD_FAILED then 'The build failed for' when Todo::BUILD_FAILED then 'The build failed for'
when Todo::MARKED then 'added a todo 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::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
end end
...@@ -148,6 +148,10 @@ module TodosHelper ...@@ -148,6 +148,10 @@ module TodosHelper
private private
def todo_action_subject(todo)
todo.self_added? ? 'yourself' : 'you'
end
def show_todo_state?(todo) def show_todo_state?(todo)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state) (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state)
end end
......
...@@ -28,6 +28,8 @@ class Blob < SimpleDelegator ...@@ -28,6 +28,8 @@ class Blob < SimpleDelegator
BlobViewer::Sketch, BlobViewer::Sketch,
BlobViewer::Balsamiq, BlobViewer::Balsamiq,
BlobViewer::Video,
BlobViewer::PDF, BlobViewer::PDF,
BlobViewer::BinarySTL, BlobViewer::BinarySTL,
......
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
...@@ -34,6 +34,7 @@ class Label < ActiveRecord::Base ...@@ -34,6 +34,7 @@ class Label < ActiveRecord::Base
scope :templates, -> { where(template: true) } scope :templates, -> { where(template: true) }
scope :with_title, ->(title) { where(title: title) } 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) def self.prioritized(project)
joins(:priorities) joins(:priorities)
......
...@@ -154,6 +154,11 @@ class Member < ActiveRecord::Base ...@@ -154,6 +154,11 @@ class Member < ActiveRecord::Base
def add_users(source, users, access_level, current_user: nil, expires_at: nil) def add_users(source, users, access_level, current_user: nil, expires_at: nil)
return [] unless users.present? 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 self.transaction do
users.map do |user| users.map do |user|
add_user( add_user(
......
...@@ -84,6 +84,10 @@ class Todo < ActiveRecord::Base ...@@ -84,6 +84,10 @@ class Todo < ActiveRecord::Base
action == BUILD_FAILED action == BUILD_FAILED
end end
def assigned?
action == ASSIGNED
end
def action_name def action_name
ACTION_NAMES[action] ACTION_NAMES[action]
end end
...@@ -117,6 +121,14 @@ class Todo < ActiveRecord::Base ...@@ -117,6 +121,14 @@ class Todo < ActiveRecord::Base
end end
end end
def self_added?
author == user
end
def self_assigned?
assigned? && self_added?
end
private private
def keep_around_commit def keep_around_commit
......
...@@ -61,7 +61,7 @@ module Boards ...@@ -61,7 +61,7 @@ module Boards
if moving_to_list.movable? if moving_to_list.movable?
moving_from_list.label_id moving_from_list.label_id
else else
project.boards.joins(:lists).merge(List.movable).pluck(:label_id) Label.on_project_boards(project.id).pluck(:label_id)
end end
Array(label_ids).compact Array(label_ids).compact
......
...@@ -330,6 +330,28 @@ module SlashCommands ...@@ -330,6 +330,28 @@ module SlashCommands
@updates[:target_branch] = branch_name if project.repository.branch_names.include?(branch_name) @updates[:target_branch] = branch_name if project.repository.branch_names.include?(branch_name)
end 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) def find_label_ids(labels_param)
label_ids_by_reference = extract_references(labels_param, :label).map(&:id) 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) labels_ids_by_name = LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute.select(:id)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.title-item.author-name .title-item.author-name
- if todo.author - if todo.author
= link_to_author(todo) = link_to_author(todo, self_added: todo.self_added?)
- else - else
(removed) (removed)
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
- else - else
(removed) (removed)
- if todo.self_assigned?
.title-item.action-name
to yourself
.title-item .title-item
&middot; &middot;
......
...@@ -27,8 +27,7 @@ ...@@ -27,8 +27,7 @@
.row .row
.col-md-8 .col-md-8
.documentation-index .documentation-index
= preserve do = markdown(@help_index)
= markdown(@help_index)
.col-md-4 .col-md-4
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
%div{ class: container_class } %div{ class: container_class }
.wiki-holder.prepend-top-default.append-bottom-default .wiki-holder.prepend-top-default.append-bottom-default
.wiki .wiki
= preserve do = render_wiki_content(@wiki_home)
= render_wiki_content(@wiki_home)
- else - else
- can_create_wiki = can?(current_user, :create_wiki, @project) - 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)] } .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
......
.file-content.video
%video{ src: blob_raw_url, controls: true, data: { setup: '{}' } }
...@@ -58,8 +58,7 @@ ...@@ -58,8 +58,7 @@
- if @issue.description.present? - if @issue.description.present?
.description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' }
.wiki .wiki
= preserve do = markdown_field(@issue, :description)
= markdown_field(@issue, :description)
%textarea.hidden.js-task-list-field %textarea.hidden.js-task-list-field
= @issue.description = @issue.description
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago')
......
...@@ -6,8 +6,7 @@ ...@@ -6,8 +6,7 @@
- if @merge_request.description.present? - if @merge_request.description.present?
.description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' } .description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' }
.wiki .wiki
= preserve do = markdown_field(@merge_request, :description)
= markdown_field(@merge_request, :description)
%textarea.hidden.js-task-list-field %textarea.hidden.js-task-list-field
= @merge_request.description = @merge_request.description
......
...@@ -43,8 +43,7 @@ ...@@ -43,8 +43,7 @@
- if @milestone.description.present? - if @milestone.description.present?
.description .description
.wiki .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? - if can?(current_user, :read_issue, @project) && @milestone.total_items_count(current_user).zero?
.alert.alert-success.prepend-top-default .alert.alert-success.prepend-top-default
......
...@@ -75,8 +75,7 @@ ...@@ -75,8 +75,7 @@
= icon('trash-o', class: 'danger-highlight') = icon('trash-o', class: 'danger-highlight')
.note-body{ class: note_editable ? 'js-task-list-container' : '' } .note-body{ class: note_editable ? 'js-task-list-container' : '' }
.note-text.md .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) = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
- if note_editable - 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 } } .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 } }
......
...@@ -24,8 +24,7 @@ ...@@ -24,8 +24,7 @@
- if release && release.description.present? - if release && release.description.present?
.description.prepend-top-default .description.prepend-top-default
.wiki .wiki
= preserve do = markdown_field(release, :description)
= markdown_field(release, :description)
.row-fixed-content.controls .row-fixed-content.controls
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name] = render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
- if @release.description.present? - if @release.description.present?
.description .description
.wiki .wiki
= preserve do = markdown_field(@release, :description)
= markdown_field(@release, :description)
- else - else
This tag has no release notes. This tag has no release notes.
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
.wiki-holder.prepend-top-default.append-bottom-default .wiki-holder.prepend-top-default.append-bottom-default
.wiki .wiki
= preserve do = render_wiki_content(@page)
= render_wiki_content(@page)
= render 'sidebar' = render 'sidebar'
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
.pull-right ##{issue.iid} .pull-right ##{issue.iid}
- if issue.description.present? - if issue.description.present?
.description.term .description.term
= preserve do = search_md_sanitize(issue, :description)
= search_md_sanitize(issue, :description)
%span.light %span.light
#{issue.project.name_with_namespace} #{issue.project.name_with_namespace}
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
.pull-right= merge_request.to_reference .pull-right= merge_request.to_reference
- if merge_request.description.present? - if merge_request.description.present?
.description.term .description.term
= preserve do = search_md_sanitize(merge_request, :description)
= search_md_sanitize(merge_request, :description)
%span.light %span.light
#{merge_request.project.name_with_namespace} #{merge_request.project.name_with_namespace}
...@@ -5,5 +5,4 @@ ...@@ -5,5 +5,4 @@
- if milestone.description.present? - if milestone.description.present?
.description.term .description.term
= preserve do = search_md_sanitize(milestone, :description)
= search_md_sanitize(milestone, :description)
...@@ -22,5 +22,4 @@ ...@@ -22,5 +22,4 @@
.note-search-result .note-search-result
.term .term
= preserve do = search_md_sanitize(note, :note)
= search_md_sanitize(note, :note)
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
= render "projects/services/#{@service.to_param}/help", subject: subject = render "projects/services/#{@service.to_param}/help", subject: subject
- elsif @service.help.present? - elsif @service.help.present?
.well .well
= preserve do = markdown @service.help
= markdown @service.help
.service-settings .service-settings
.form-group .form-group
......
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
%span.remaining-days= remaining_days %span.remaining-days= remaining_days
- if !project || can?(current_user, :read_issue, project) - if !project || can?(current_user, :read_issue, project)
.block .block.issues
.sidebar-collapsed-icon .sidebar-collapsed-icon
%strong %strong
= icon('hashtag', 'aria-hidden': 'true') = icon('hashtag', 'aria-hidden': 'true')
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
Closed: Closed:
= milestone.issues_visible_to_user(current_user).closed.count = milestone.issues_visible_to_user(current_user).closed.count
.block .block.merge-requests
.sidebar-collapsed-icon .sidebar-collapsed-icon
%strong %strong
= icon('exclamation', 'aria-hidden': 'true') = icon('exclamation', 'aria-hidden': 'true')
......
---
title: Improve text on todo list when the todo action comes from yourself
merge_request: 10594
author: Jacopo Beschi @jacopo-beschi
---
title: Add board_move slash command
merge_request: 10433
author: Alex Sanford
---
title: Add index on ci_runners.contacted_at
merge_request: 10876
author: blackst0ne
---
title: Display video blobs in-line like images
merge_request:
author:
---
title: Use GitLab Pages v0.4.1
merge_request:
author:
---
title: Ensure the chat notifications service properly saves the "Notify only default branch" setting
merge_request: 10959
author:
...@@ -36,10 +36,10 @@ if Rails.env.test? ...@@ -36,10 +36,10 @@ if Rails.env.test?
RspecProfiling::Collectors::PSQL.prepend(RspecProfilingExt::PSQL) RspecProfiling::Collectors::PSQL.prepend(RspecProfilingExt::PSQL)
config.collector = RspecProfiling::Collectors::PSQL config.collector = RspecProfiling::Collectors::PSQL
end end
end
if ENV.has_key?('CI') && ENV['GITLAB_DATABASE'] == 'postgresql' if ENV.key?('CI')
RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git)
RspecProfiling::Run.prepend(RspecProfilingExt::Run) RspecProfiling::Run.prepend(RspecProfilingExt::Run)
end
end end
end end
...@@ -99,5 +99,7 @@ Rails.application.routes.draw do ...@@ -99,5 +99,7 @@ Rails.application.routes.draw do
end end
end end
draw :test if Rails.env.test?
get '*unmatched_route', to: 'application#route_not_found' get '*unmatched_route', to: 'application#route_not_found'
end end
get '/unicorn_test/pid' => 'unicorn_test#pid'
post '/unicorn_test/kill' => 'unicorn_test#kill'
# 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
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -296,6 +296,7 @@ ActiveRecord::Schema.define(version: 20170426175636) do ...@@ -296,6 +296,7 @@ ActiveRecord::Schema.define(version: 20170426175636) do
t.boolean "locked", default: false, null: false t.boolean "locked", default: false, null: false
end 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", ["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", ["locked"], name: "index_ci_runners_on_locked", using: :btree
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
......
...@@ -65,14 +65,14 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server ...@@ -65,14 +65,14 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
# #
# Example: 'Paris' or 'Acme, Ltd.' # Example: 'Paris' or 'Acme, Ltd.'
label: 'LDAP' label: 'LDAP'
# Example: 'ldap.mydomain.com' # Example: 'ldap.mydomain.com'
host: '_your_ldap_server' host: '_your_ldap_server'
# This port is an example, it is sometimes different but it is always an integer and not a string # This port is an example, it is sometimes different but it is always an integer and not a string
port: 389 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" method: 'plain' # "tls" or "ssl" or "plain"
# Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com' # 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' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user' password: '_the_password_of_the_bind_user'
......
...@@ -70,3 +70,27 @@ All the docs follow the same [styleguide](doc_styleguide.md). ...@@ -70,3 +70,27 @@ All the docs follow the same [styleguide](doc_styleguide.md).
### Markdown ### 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. 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.
...@@ -100,7 +100,7 @@ On failure, the endpoint will return a `500` HTTP status code. On success, the e ...@@ -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 will return a valid successful HTTP status code, and a `success` message. Ideally your
uptime monitoring should look for the success message. 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 [ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com [pingdom]: https://www.pingdom.com
[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html [nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
......
...@@ -36,3 +36,4 @@ do. ...@@ -36,3 +36,4 @@ do.
| `/remove_time_spent` | Remove time spent | | `/remove_time_spent` | Remove time spent |
| `/target_branch <Branch Name>` | Set target branch for current merge request | | `/target_branch <Branch Name>` | Set target branch for current merge request |
| `/award :emoji:` | Toggle award for :emoji: | | `/award :emoji:` | Toggle award for :emoji: |
| `/board_move ~column` | Move issue to column on the board |
...@@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -21,7 +21,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(response_headers['Content-Type']).to have_content("application/atom+xml") 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("title", text: "#{@project.name}:master commits")
expect(body).to have_selector("author email", text: commit.author_email) 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 end
step 'I click on tag link' do step 'I click on tag link' do
......
...@@ -2,7 +2,7 @@ desc 'Security check via brakeman' ...@@ -2,7 +2,7 @@ desc 'Security check via brakeman'
task :brakeman do task :brakeman do
# We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge # We get 0 warnings at level 'w3' but we would like to reach 'w2'. Merge
# requests are welcome! # 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' puts 'Security check succeed'
else else
puts 'Security check failed' puts 'Security check failed'
......
#!/bin/sh
. scripts/utils.sh . scripts/utils.sh
export SETUP_DB=${SETUP_DB:-true} export SETUP_DB=${SETUP_DB:-true}
...@@ -32,7 +30,7 @@ sed -i 's/localhost/redis/g' config/resque.yml ...@@ -32,7 +30,7 @@ sed -i 's/localhost/redis/g' config/resque.yml
cp config/gitlab.yml.example config/gitlab.yml cp config/gitlab.yml.example config/gitlab.yml
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
retry bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
fi fi
# Only install knapsack after bundle install! Otherwise oddly some native # Only install knapsack after bundle install! Otherwise oddly some native
......
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
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
...@@ -3,6 +3,57 @@ require 'rails_helper' ...@@ -3,6 +3,57 @@ require 'rails_helper'
describe Projects::BlobController do describe Projects::BlobController do
let(:project) { create(:project, :public, :repository) } 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 describe 'GET diff' do
let(:user) { create(:user) } let(:user) { create(:user) }
......
...@@ -53,7 +53,7 @@ describe "User Feed", feature: true do ...@@ -53,7 +53,7 @@ describe "User Feed", feature: true do
end end
it 'has XHTML summaries in issue descriptions' do 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 end
it 'has XHTML summaries in notes' do it 'has XHTML summaries in notes' do
......
...@@ -78,11 +78,10 @@ feature 'Project milestone', :feature do ...@@ -78,11 +78,10 @@ feature 'Project milestone', :feature do
it 'shows the total MR and issue counts' do it 'shows the total MR and issue counts' do
find('.milestone-sidebar .block', match: :first) find('.milestone-sidebar .block', match: :first)
blocks = all('.milestone-sidebar .block')
aggregate_failures 'MR and issue blocks' do aggregate_failures 'MR and issue blocks' do
expect(blocks[3]).to have_content 1 expect(find('.milestone-sidebar .block.issues')).to have_content 1
expect(blocks[4]).to have_content 0 expect(find('.milestone-sidebar .block.merge-requests')).to have_content 0
end end
end end
end end
......
...@@ -9,7 +9,7 @@ RSpec.shared_examples "protected branches > access control > CE" do ...@@ -9,7 +9,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
allowed_to_push_button = find(".js-allowed-to-push") allowed_to_push_button = find(".js-allowed-to-push")
unless allowed_to_push_button.text == access_type_name 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 } within(".dropdown.open .dropdown-menu") { click_on access_type_name }
end end
end end
......
...@@ -8,7 +8,7 @@ feature 'Projected Branches', feature: true, js: true do ...@@ -8,7 +8,7 @@ feature 'Projected Branches', feature: true, js: true do
before { login_as(user) } before { login_as(user) }
def set_protected_branch_name(branch_name) 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) find(".dropdown-input-field").set(branch_name)
click_on("Create wildcard #{branch_name}") click_on("Create wildcard #{branch_name}")
end end
......
...@@ -45,8 +45,8 @@ describe 'Dashboard > User filters todos', feature: true, js: true do ...@@ -45,8 +45,8 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
wait_for_ajax wait_for_ajax
expect(find('.todos-list')).to have_content user_1.name expect(find('.todos-list')).to have_content 'merge request'
expect(find('.todos-list')).not_to have_content user_2.name expect(find('.todos-list')).not_to have_content 'issue'
end end
it "shows only authors of existing todos" do it "shows only authors of existing todos" do
......
...@@ -99,6 +99,83 @@ describe 'Dashboard Todos', feature: true do ...@@ -99,6 +99,83 @@ describe 'Dashboard Todos', feature: true do
end end
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 context 'User has done todos', js: true do
before do before do
create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author) create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author)
......
...@@ -56,7 +56,7 @@ describe EventsHelper do ...@@ -56,7 +56,7 @@ describe EventsHelper do
it 'preserves code color scheme' do it 'preserves code color scheme' do
input = "```ruby\ndef test\n 'hello world'\nend\n```" 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><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
"</code></pre>" "</code></pre>"
expect(helper.event_note(input)).to eq(expected) expect(helper.event_note(input)).to eq(expected)
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::Checks::ChangeAccess, lib: true do describe Gitlab::Checks::ForcePush, lib: true do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
context "exit code checking" do context "exit code checking" do
it "does not raise a runtime error if the `popen` call to git returns a zero exit code" 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]) 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 end
it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do 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]) 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 end
end end
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
require 'spec_helper' require 'spec_helper'
require 'email_spec' require 'email_spec'
describe Notify, "merge request notifications" do describe Emails::MergeRequests do
include EmailSpec::Matchers include EmailSpec::Matchers
describe "#resolved_all_discussions_email" do describe "#resolved_all_discussions_email" do
......
require 'spec_helper' require 'spec_helper'
require 'email_spec' require 'email_spec'
describe Notify do describe Emails::Profile do
include EmailSpec::Matchers include EmailSpec::Matchers
include_context 'gitlab email notification' include_context 'gitlab email notification'
...@@ -15,106 +15,104 @@ describe Notify do ...@@ -15,106 +15,104 @@ describe Notify do
end end
end end
describe 'profile notifications' do describe 'for new users, the email' do
describe 'for new users, the email' do let(:example_site_path) { root_path }
let(:example_site_path) { root_path } let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) }
let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } let(:token) { 'kETLwRaayvigPq_x3SNM' }
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 'an email sent from GitLab'
it_behaves_like 'a new user email' it_behaves_like 'a new user email'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'a user cannot unsubscribe through footer link'
it 'contains the password text' do it 'contains the password text' do
is_expected.to have_body_text /Click here to set your password/ is_expected.to have_body_text /Click here to set your password/
end end
it 'includes a link for user to set password' do it 'includes a link for user to set password' do
params = "reset_password_token=#{token}" params = "reset_password_token=#{token}"
is_expected.to have_body_text( is_expected.to have_body_text(
%r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}} %r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}}
) )
end end
it 'explains the reset link expiration' do 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(/This link is valid for \d+ (hours?|days?)/)
is_expected.to have_body_text(new_user_password_url) is_expected.to have_body_text(new_user_password_url)
is_expected.to have_body_text(/\?user_email=.*%40.*/) is_expected.to have_body_text(/\?user_email=.*%40.*/)
end
end end
end
describe 'for users that signed up, the email' do describe 'for users that signed up, the email' do
let(:example_site_path) { root_path } let(:example_site_path) { root_path }
let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } 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 'an email sent from GitLab'
it_behaves_like 'a new user email' it_behaves_like 'a new user email'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'a user cannot unsubscribe through footer link'
it 'does not contain the new user\'s password' do it 'does not contain the new user\'s password' do
is_expected.not_to have_body_text /password/ is_expected.not_to have_body_text /password/
end
end end
end
describe 'user added ssh key' do describe 'user added ssh key' do
let(:key) { create(:personal_key) } 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 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'a user cannot unsubscribe through footer link'
it 'is sent to the new user' do it 'is sent to the new user' do
is_expected.to deliver_to key.user.email is_expected.to deliver_to key.user.email
end end
it 'has the correct subject' do it 'has the correct subject' do
is_expected.to have_subject /^SSH key was added to your account$/i is_expected.to have_subject /^SSH key was added to your account$/i
end end
it 'contains the new ssh key title' do it 'contains the new ssh key title' do
is_expected.to have_body_text /#{key.title}/ is_expected.to have_body_text /#{key.title}/
end end
it 'includes a link to ssh keys page' do it 'includes a link to ssh keys page' do
is_expected.to have_body_text /#{profile_keys_path}/ is_expected.to have_body_text /#{profile_keys_path}/
end end
context 'with SSH key that does not exist' do context 'with SSH key that does not exist' do
it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error } it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error }
end
end end
end
describe 'user added email' do describe 'user added email' do
let(:email) { create(:email) } 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 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'a user cannot unsubscribe through footer link'
it 'is sent to the new user' do it 'is sent to the new user' do
is_expected.to deliver_to email.user.email is_expected.to deliver_to email.user.email
end end
it 'has the correct subject' do it 'has the correct subject' do
is_expected.to have_subject /^Email was added to your account$/i is_expected.to have_subject /^Email was added to your account$/i
end end
it 'contains the new email address' do it 'contains the new email address' do
is_expected.to have_body_text /#{email.email}/ is_expected.to have_body_text /#{email.email}/
end end
it 'includes a link to emails page' do it 'includes a link to emails page' do
is_expected.to have_body_text /#{profile_emails_path}/ is_expected.to have_body_text /#{profile_emails_path}/
end
end end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Issue, "Awardable" do describe Awardable do
let!(:issue) { create(:issue) } let!(:issue) { create(:issue) }
let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) } let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) }
describe "Associations" do describe "Associations" do
subject { build(:issue) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) } it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
end end
......
require 'spec_helper' require 'spec_helper'
describe DiffDiscussion, DiscussionOnDiff, model: true do describe DiscussionOnDiff, model: true do
subject { create(:diff_note_on_merge_request).to_discussion } subject { create(:diff_note_on_merge_request).to_discussion }
describe "#truncated_diff_lines" do describe "#truncated_diff_lines" do
...@@ -8,9 +8,9 @@ describe DiffDiscussion, DiscussionOnDiff, model: true do ...@@ -8,9 +8,9 @@ describe DiffDiscussion, DiscussionOnDiff, model: true do
context "when diff is greater than allowed number of truncated diff lines " do context "when diff is greater than allowed number of truncated diff lines " do
it "returns fewer 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
end end
......
require 'spec_helper' require 'spec_helper'
describe Issue, "Issuable" do describe Issuable do
let(:issuable_class) { Issue }
let(:issue) { create(:issue) } let(:issue) { create(:issue) }
let(:user) { create(:user) } let(:user) { create(:user) }
describe "Associations" do describe "Associations" do
subject { build(:issue) }
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:author) } it { is_expected.to belong_to(:author) }
it { is_expected.to belong_to(:assignee) } it { is_expected.to belong_to(:assignee) }
...@@ -23,10 +26,14 @@ describe Issue, "Issuable" do ...@@ -23,10 +26,14 @@ describe Issue, "Issuable" do
end end
describe 'Included modules' do describe 'Included modules' do
let(:described_class) { issuable_class }
it { is_expected.to include_module(Awardable) } it { is_expected.to include_module(Awardable) }
end end
describe "Validation" do describe "Validation" do
subject { build(:issue) }
before do before do
allow(subject).to receive(:set_iid).and_return(false) allow(subject).to receive(:set_iid).and_return(false)
end end
...@@ -39,9 +46,11 @@ describe Issue, "Issuable" do ...@@ -39,9 +46,11 @@ describe Issue, "Issuable" do
end end
describe "Scope" do describe "Scope" do
it { expect(described_class).to respond_to(:opened) } subject { build(:issue) }
it { expect(described_class).to respond_to(:closed) }
it { expect(described_class).to respond_to(:assigned) } 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 end
describe 'author_name' do describe 'author_name' do
...@@ -115,16 +124,16 @@ describe Issue, "Issuable" do ...@@ -115,16 +124,16 @@ describe Issue, "Issuable" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") } let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
it 'returns notes with a matching title' do 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]) to eq([searchable_issue])
end end
it 'returns notes with a partially matching title' do 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 end
it 'returns notes with a matching title regardless of the casing' do 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]) to eq([searchable_issue])
end end
end end
...@@ -135,31 +144,31 @@ describe Issue, "Issuable" do ...@@ -135,31 +144,31 @@ describe Issue, "Issuable" do
end end
it 'returns notes with a matching title' do 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]) to eq([searchable_issue])
end end
it 'returns notes with a partially matching title' do 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 end
it 'returns notes with a matching title regardless of the casing' do 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]) to eq([searchable_issue])
end end
it 'returns notes with a matching description' do 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]) to eq([searchable_issue])
end end
it 'returns notes with a partially matching description' do 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]) to eq([searchable_issue])
end end
it 'returns notes with a matching description regardless of the casing' do 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]) to eq([searchable_issue])
end end
end end
......
require 'spec_helper' 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!(:active_diff_note1) { create(:diff_note_on_merge_request) }
let(:project) { active_diff_note1.project } let(:project) { active_diff_note1.project }
subject { active_diff_note1.noteable } subject { active_diff_note1.noteable }
......
require 'spec_helper' require 'spec_helper'
describe Issue, 'RelativePositioning' do describe RelativePositioning do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:issue1) { create(:issue, project: project) } let(:issue1) { create(:issue, project: project) }
......
require 'spec_helper' require 'spec_helper'
describe Issue, 'Spammable' do describe Spammable do
let(:issue) { create(:issue, description: 'Test Desc.') } let(:issue) { create(:issue, description: 'Test Desc.') }
describe 'Associations' do describe 'Associations' do
subject { build(:issue) }
it { is_expected.to have_one(:user_agent_detail).dependent(:destroy) } it { is_expected.to have_one(:user_agent_detail).dependent(:destroy) }
end end
......
require 'spec_helper' require 'spec_helper'
describe Milestone, "StripAttribute" do describe StripAttribute do
let(:milestone) { create(:milestone) } let(:milestone) { create(:milestone) }
describe ".strip_attributes" do describe ".strip_attributes" do
......
...@@ -390,13 +390,15 @@ describe Member, models: true do ...@@ -390,13 +390,15 @@ describe Member, models: true do
%w[project group].each do |source_type| %w[project group].each do |source_type|
context "when source is a #{source_type}" do context "when source is a #{source_type}" do
let!(:source) { create(source_type, :public, :access_requestable) } let!(:source) { create(source_type, :public, :access_requestable) }
let!(:user) { create(:user) }
let!(:admin) { create(:admin) } let!(:admin) { create(:admin) }
let(:user1) { create(:user) }
let(:user2) { create(:user) }
it 'returns a <Source>Member objects' do 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).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_a "#{source_type.classify}Member".constantize
expect(members.first).to be_persisted expect(members.first).to be_persisted
end end
......
...@@ -125,4 +125,50 @@ describe Todo, models: true do ...@@ -125,4 +125,50 @@ describe Todo, models: true do
expect(subject.target_reference).to eq issue.to_reference(full: true) expect(subject.target_reference).to eq issue.to_reference(full: true)
end end
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 end
require 'spec_helper' require 'spec_helper'
describe IssuePolicy, models: true do describe IssuePolicy, models: true do
let(:user) { create(:user) } let(:guest) { create(:user) }
let(:author) { create(:user) }
describe '#rules' do let(:assignee) { create(:user) }
context 'using a regular issue' do let(:reporter) { create(:user) }
let(:project) { create(:empty_project, :public) } let(:group) { create(:group, :public) }
let(:issue) { create(:issue, project: project) } let(:reporter_from_group_link) { create(:user) }
let(:policies) { described_class.abilities(user, issue).to_set }
def permissions(user, issue)
context 'with a regular user' do described_class.abilities(user, issue).to_set
it 'includes the read_issue permission' do end
expect(policies).to include(:read_issue)
end context 'a private project' do
let(:non_member) { create(:user) }
it 'does not include the admin_issue permission' do let(:project) { create(:empty_project, :private) }
expect(policies).not_to include(:admin_issue) let(:issue) { create(:issue, project: project, assignee: assignee, author: author) }
end let(:issue_no_assignee) { create(:issue, project: project) }
it 'does not include the update_issue permission' do before do
expect(policies).not_to include(:update_issue) project.team << [guest, :guest]
end project.team << [author, :guest]
end 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 context 'with confidential issues' do
before do let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) }
project.team << [user, :reporter] let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
end
it 'includes the read_issue permission' do it 'does not allow non-members to read confidential issues' do
expect(policies).to include(:read_issue) expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue)
end 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 it 'allows reporters to read, update, and admin confidential issues' do
expect(policies).to include(:admin_issue) expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue)
end expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue)
end
it 'includes the update_issue permission' do it 'allows reporters from group links to read, update, and admin confidential issues' do
expect(policies).to include(:update_issue) expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue)
end expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue)
end end
context 'with a user that is a project guest' do it 'allows issue authors to read and update their confidential issues' do
before do expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue)
project.team << [user, :guest] expect(permissions(author, confidential_issue)).not_to include(:admin_issue)
end
it 'includes the read_issue permission' do expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue)
expect(policies).to include(:read_issue) end
end
it 'does not include the admin_issue permission' do it 'allows issue assignees to read and update their confidential issues' do
expect(policies).not_to include(:admin_issue) expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue)
end expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue)
it 'does not include the update_issue permission' do expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue)
expect(policies).not_to include(:update_issue)
end
end end
end end
end
context 'using a confidential issue' do context 'a public project' do
let(:issue) { create(:issue, :confidential) } 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 before do
let(:policies) { described_class.abilities(user, issue).to_set } project.team << [guest, :guest]
project.team << [reporter, :reporter]
it 'does not include the read_issue permission' do group.add_reporter(reporter_from_group_link)
expect(policies).not_to include(:read_issue)
end
it 'does not include the admin_issue permission' do create(:project_group_link, group: group, project: project)
expect(policies).not_to include(:admin_issue) end
end
it 'does not include the update_issue permission' do it 'allows guests to read issues' do
expect(policies).not_to include(:update_issue) expect(permissions(guest, issue)).to include(:read_issue)
end expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue)
end
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 it 'allows issue authors to read and update their issues' do
let(:policies) { described_class.abilities(user, issue).to_set } expect(permissions(author, issue)).to include(:read_issue, :update_issue)
expect(permissions(author, issue)).not_to include(:admin_issue)
before do expect(permissions(author, issue_no_assignee)).to include(:read_issue)
issue.project.team << [user, :reporter] expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue)
end 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(permissions(assignee, issue_no_assignee)).to include(:read_issue)
expect(policies).to include(:read_issue) expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue)
end end
it 'includes the admin_issue permission' do context 'with confidential issues' do
expect(policies).to include(:admin_issue) let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) }
end let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
it 'includes the update_issue permission' do it 'does not allow guests to read confidential issues' do
expect(policies).to include(:update_issue) expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue)
end expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue)
end end
context 'without a user' do it 'allows reporters to read, update, and admin confidential issues' do
let(:policies) { described_class.abilities(nil, issue).to_set } 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 it 'allows issue authors to read and update their confidential issues' do
expect(policies).not_to include(:read_issue) expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue)
end 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 it 'allows issue assignees to read and update their confidential issues' do
expect(policies).not_to include(:admin_issue) expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue)
end expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue)
it 'does not include the update_issue permission' do expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue)
expect(policies).not_to include(:update_issue)
end
end end
end end
end end
......
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
require 'spec_helper' require 'spec_helper'
describe API::API do describe 'doorkeeper access' do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: 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" } let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" }
......
require 'spec_helper' require 'spec_helper'
describe API::API do describe 'OAuth tokens' do
context 'Resource Owner Password Credentials' do context 'Resource Owner Password Credentials' do
def request_oauth_token(user) def request_oauth_token(user)
post '/oauth/token', username: user.username, password: user.password, grant_type: 'password' post '/oauth/token', username: user.username, password: user.password, grant_type: 'password'
......
require 'spec_helper' require 'spec_helper'
describe Projects::EnvironmentsController, :routing do describe 'environments routing', :routing do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:environment) do let(:environment) do
......
require "spec_helper" require "spec_helper"
describe Profiles::NotificationsController do describe "notifications routing" do
describe "routing" do it "routes to #show" do
it "routes to #show" do expect(get("/profile/notifications")).to route_to("profiles/notifications#show")
expect(get("/profile/notifications")).to route_to("profiles/notifications#show") end
end
it "routes to #update" do it "routes to #update" do
expect(put("/profile/notifications")).to route_to("profiles/notifications#update") expect(put("/profile/notifications")).to route_to("profiles/notifications#update")
end
end end
end end
require 'spec_helper.rb' require 'spec_helper.rb'
class DummyService < Issues::BaseService describe Issues::ResolveDiscussions, services: true do
include ::Issues::ResolveDiscussions class DummyService < Issues::BaseService
include ::Issues::ResolveDiscussions
def initialize(*args) def initialize(*args)
super super
filter_resolve_discussion_params filter_resolve_discussion_params
end
end end
end
describe DummyService, services: true do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -23,7 +23,7 @@ describe DummyService, services: true do ...@@ -23,7 +23,7 @@ describe DummyService, services: true do
let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "other") } let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "other") }
describe "#merge_request_for_resolving_discussion" do 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 it "finds the merge request" do
expect(service.merge_request_to_resolve_discussions_of).to eq(merge_request) expect(service.merge_request_to_resolve_discussions_of).to eq(merge_request)
...@@ -43,7 +43,7 @@ describe DummyService, services: true do ...@@ -43,7 +43,7 @@ describe DummyService, services: true do
describe "#discussions_to_resolve" do describe "#discussions_to_resolve" do
it "contains a single discussion when matching merge request and discussion are passed" do it "contains a single discussion when matching merge request and discussion are passed" do
service = described_class.new( service = DummyService.new(
project, project,
user, user,
discussion_to_resolve: discussion.id, discussion_to_resolve: discussion.id,
...@@ -61,7 +61,7 @@ describe DummyService, services: true do ...@@ -61,7 +61,7 @@ describe DummyService, services: true do
noteable: merge_request, noteable: merge_request,
project: merge_request.target_project, project: merge_request.target_project,
line_number: 15)]) line_number: 15)])
service = described_class.new( service = DummyService.new(
project, project,
user, user,
merge_request_to_resolve_discussions_of: merge_request.iid merge_request_to_resolve_discussions_of: merge_request.iid
...@@ -79,7 +79,7 @@ describe DummyService, services: true do ...@@ -79,7 +79,7 @@ describe DummyService, services: true do
project: merge_request.target_project, project: merge_request.target_project,
line_number: 15, line_number: 15,
)]) )])
service = described_class.new( service = DummyService.new(
project, project,
user, user,
merge_request_to_resolve_discussions_of: merge_request.iid merge_request_to_resolve_discussions_of: merge_request.iid
...@@ -92,7 +92,7 @@ describe DummyService, services: true do ...@@ -92,7 +92,7 @@ describe DummyService, services: true do
end end
it "is empty when a discussion and another merge request are passed" do it "is empty when a discussion and another merge request are passed" do
service = described_class.new( service = DummyService.new(
project, project,
user, user,
discussion_to_resolve: discussion.id, discussion_to_resolve: discussion.id,
......
...@@ -52,7 +52,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -52,7 +52,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unassign command' do shared_examples 'unassign command' do
it 'populates assignee_id: nil if content contains /unassign' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(assignee_id: nil) expect(updates).to eq(assignee_id: nil)
...@@ -70,7 +70,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -70,7 +70,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'remove_milestone command' do shared_examples 'remove_milestone command' do
it 'populates milestone_id: nil if content contains /remove_milestone' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(milestone_id: nil) expect(updates).to eq(milestone_id: nil)
...@@ -108,7 +108,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -108,7 +108,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unlabel command' do shared_examples 'unlabel command' do
it 'fetches label ids and populates remove_label_ids if content contains /unlabel' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(remove_label_ids: [inprogress.id]) expect(updates).to eq(remove_label_ids: [inprogress.id])
...@@ -117,7 +117,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -117,7 +117,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'multiple unlabel command' do shared_examples 'multiple unlabel command' do
it 'fetches label ids and populates remove_label_ids if content contains mutiple /unlabel' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(remove_label_ids: [inprogress.id, bug.id]) expect(updates).to eq(remove_label_ids: [inprogress.id, bug.id])
...@@ -126,7 +126,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -126,7 +126,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unlabel command with no argument' do shared_examples 'unlabel command with no argument' do
it 'populates label_ids: [] if content contains /unlabel with no arguments' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(label_ids: []) expect(updates).to eq(label_ids: [])
...@@ -135,7 +135,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -135,7 +135,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'relabel command' do shared_examples 'relabel command' do
it 'populates label_ids: [] if content contains /relabel' 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 inprogress # populate the label
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
...@@ -187,7 +187,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -187,7 +187,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'remove_due_date command' do shared_examples 'remove_due_date command' do
it 'populates due_date: nil if content contains /remove_due_date' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(due_date: nil) expect(updates).to eq(due_date: nil)
...@@ -204,7 +204,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -204,7 +204,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unwip command' do shared_examples 'unwip command' do
it 'returns wip_event: "unwip" if content contains /wip' 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) _, updates = service.execute(content, issuable)
expect(updates).to eq(wip_event: 'unwip') expect(updates).to eq(wip_event: 'unwip')
...@@ -727,5 +727,75 @@ describe SlashCommands::InterpretService, services: true do ...@@ -727,5 +727,75 @@ describe SlashCommands::InterpretService, services: true do
end end
end 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
end end
...@@ -13,8 +13,9 @@ rspec_profiling_is_configured = ...@@ -13,8 +13,9 @@ rspec_profiling_is_configured =
ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING_POSTGRES_URL'] ||
ENV['RSPEC_PROFILING'] ENV['RSPEC_PROFILING']
branch_can_be_profiled = branch_can_be_profiled =
ENV['CI_COMMIT_REF_NAME'] == 'master' || ENV['GITLAB_DATABASE'] == 'postgresql' &&
ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/ (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) if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled)
require 'rspec_profiling/rspec' require 'rspec_profiling/rspec'
......
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment