summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorJacob Vosmaer <contact@jacobvosmaer.nl>2016-04-12 18:23:42 +0200
committerJacob Vosmaer <contact@jacobvosmaer.nl>2016-04-12 18:23:42 +0200
commita1c8fdfbeef5a74dfdf86f1e7954a7a8eb8577eb (patch)
tree528c69cf876d2473fd9cfbfcf94d58a938fe0f7f /spec
parentab9dfa8fd681ac558cf988aa2cdb5bd69feea757 (diff)
parent2082879d2f3f91b038863f7c67c658d678924564 (diff)
downloadgitlab-ce-a1c8fdfbeef5a74dfdf86f1e7954a7a8eb8577eb.tar.gz
Merge branch 'master' of https://gitlab.com/gitlab-org/gitlab-ce into git-http-controller
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb23
-rw-r--r--spec/controllers/admin/users_controller_spec.rb15
-rw-r--r--spec/controllers/ci/projects_controller_spec.rb21
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb6
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb14
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb49
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb107
-rw-r--r--spec/controllers/projects_controller_spec.rb22
-rw-r--r--spec/controllers/root_controller_spec.rb22
-rw-r--r--spec/controllers/sessions_controller_spec.rb101
-rw-r--r--spec/factories/file_uploader.rb20
-rw-r--r--spec/factories/forked_project_links.rb5
-rw-r--r--spec/factories_spec.rb16
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/dashboard_issues_spec.rb54
-rw-r--r--spec/features/issues/award_emoji_spec.rb64
-rw-r--r--spec/features/issues/filter_by_milestone_spec.rb42
-rw-r--r--spec/features/issues/filter_issues_spec.rb119
-rw-r--r--spec/features/issues/update_issues_spec.rb117
-rw-r--r--spec/features/issues_spec.rb79
-rw-r--r--spec/features/markdown_spec.rb3
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb33
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb38
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb6
-rw-r--r--spec/features/projects/badges/list_spec.rb34
-rw-r--r--spec/features/search_spec.rb43
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb78
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb63
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb93
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml42
-rw-r--r--spec/fixtures/emails/valid_reply.eml4
-rw-r--r--spec/helpers/form_helper_spec.rb46
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb7
-rw-r--r--spec/helpers/issues_helper_spec.rb23
-rw-r--r--spec/helpers/preferences_helper_spec.rb4
-rw-r--r--spec/javascripts/fixtures/zen_mode.html.haml2
-rw-r--r--spec/javascripts/issue_spec.js.coffee6
-rw-r--r--spec/lib/award_emoji_spec.rb19
-rw-r--r--spec/lib/banzai/filter/gollum_tags_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/image_link_filter_spec.rb24
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb8
-rw-r--r--spec/lib/banzai/pipeline/wiki_pipeline_spec.rb6
-rw-r--r--spec/lib/extracts_path_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/build_spec.rb103
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb117
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb23
-rw-r--r--spec/lib/gitlab/fogbugz_import/client_spec.rb24
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb66
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb26
-rw-r--r--spec/lib/gitlab/ldap/access_spec.rb27
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb71
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics_spec.rb44
-rw-r--r--spec/lib/gitlab/saml/user_spec.rb166
-rw-r--r--spec/mailers/notify_spec.rb66
-rw-r--r--spec/mailers/shared/notify.rb78
-rw-r--r--spec/models/application_setting_spec.rb1
-rw-r--r--spec/models/concerns/issuable_spec.rb1
-rw-r--r--spec/models/event_spec.rb78
-rw-r--r--spec/models/hooks/system_hook_spec.rb54
-rw-r--r--spec/models/merge_request_spec.rb8
-rw-r--r--spec/models/project_services/builds_email_service_spec.rb24
-rw-r--r--spec/models/project_services/slack_service/issue_message_spec.rb10
-rw-r--r--spec/models/project_spec.rb15
-rw-r--r--spec/models/repository_spec.rb81
-rw-r--r--spec/models/user_spec.rb7
-rw-r--r--spec/requests/api/issues_spec.rb11
-rw-r--r--spec/requests/api/labels_spec.rb29
-rw-r--r--spec/requests/api/merge_requests_spec.rb11
-rw-r--r--spec/requests/api/milestones_spec.rb27
-rw-r--r--spec/requests/api/notes_spec.rb61
-rw-r--r--spec/requests/api/project_members_spec.rb20
-rw-r--r--spec/requests/api/projects_spec.rb72
-rw-r--r--spec/requests/api/tags_spec.rb17
-rw-r--r--spec/services/git_push_service_spec.rb36
-rw-r--r--spec/services/issues/move_service_spec.rb35
-rw-r--r--spec/services/notes/delete_service_spec.rb15
-rw-r--r--spec/services/projects/import_service_spec.rb17
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb32
-rw-r--r--spec/services/todo_service_spec.rb77
-rw-r--r--spec/support/carrierwave.rb7
-rw-r--r--spec/support/filter_spec_helper.rb2
-rw-r--r--spec/support/markdown_feature.rb2
-rw-r--r--spec/support/matchers/markdown_matchers.rb9
-rw-r--r--spec/support/test_env.rb1
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb4
-rw-r--r--spec/workers/merge_worker_spec.rb2
-rw-r--r--spec/workers/project_cache_worker_spec.rb27
89 files changed, 2727 insertions, 375 deletions
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
new file mode 100644
index 00000000000..2ba0d489197
--- /dev/null
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Admin::ProjectsController do
+ let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+
+ before do
+ sign_in(create(:admin))
+ end
+
+ describe 'GET /projects' do
+ render_views
+
+ it 'retrieves the project for the given visibility level' do
+ get :index, visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]
+ expect(response.body).to match(project.name)
+ end
+
+ it 'does not retrieve the project' do
+ get :index, visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]
+ expect(response.body).to_not match(project.name)
+ end
+ end
+end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 5b1f65d7aff..9ef8ba1b097 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -1,15 +1,14 @@
require 'spec_helper'
describe Admin::UsersController do
- let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
before do
- sign_in(admin)
+ sign_in(create(:admin))
end
describe 'DELETE #user with projects' do
- let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before do
project.team << [user, :developer]
@@ -23,8 +22,6 @@ describe Admin::UsersController do
end
describe 'PUT block/:id' do
- let(:user) { create(:user) }
-
it 'blocks user' do
put :block, id: user.username
user.reload
@@ -50,8 +47,6 @@ describe Admin::UsersController do
end
context 'manually blocked users' do
- let(:user) { create(:user) }
-
before do
user.block
end
@@ -66,8 +61,6 @@ describe Admin::UsersController do
end
describe 'PUT unlock/:id' do
- let(:user) { create(:user) }
-
before do
request.env["HTTP_REFERER"] = "/"
user.lock_access!
@@ -95,8 +88,6 @@ describe Admin::UsersController do
end
describe 'PATCH disable_two_factor' do
- let(:user) { create(:user) }
-
it 'disables 2FA for the user' do
expect(user).to receive(:disable_two_factor!)
allow(subject).to receive(:user).and_return(user)
diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb
index db0748f323f..5022a3e2c80 100644
--- a/spec/controllers/ci/projects_controller_spec.rb
+++ b/spec/controllers/ci/projects_controller_spec.rb
@@ -5,6 +5,27 @@ describe Ci::ProjectsController do
let!(:project) { create(:project, visibility, ci_id: 1) }
let(:ci_id) { project.ci_id }
+ describe '#index' do
+ context 'user signed in' do
+ before do
+ sign_in(create(:user))
+ get(:index)
+ end
+
+ it 'redirects to /' do
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context 'user not signed in' do
+ before { get(:index) }
+
+ it 'redirects to sign in page' do
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+ end
+
##
# Specs for *deprecated* CI badge
#
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index eb0c6ac6d80..b0793cb1655 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -23,5 +23,11 @@ describe Groups::MilestonesController do
expect(response).to redirect_to(group_milestone_path(group, title.to_slug.to_s, title: title))
expect(Milestone.where(title: title).count).to eq(2)
end
+
+ it "redirects to new when there are no project ids" do
+ post :create, group_id: group.id, milestone: { title: title, project_ids: [""] }
+ expect(response).to render_template :new
+ expect(assigns(:milestone).errors).not_to be_nil
+ end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 98ae424ed7c..8ad73472117 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -93,6 +93,20 @@ describe Projects::BranchesController do
end
end
+ describe "POST destroy with HTML format" do
+ render_views
+
+ it 'returns 303' do
+ post :destroy,
+ format: :html,
+ id: 'foo/bar/baz',
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param
+
+ expect(response.status).to eq(303)
+ end
+ end
+
describe "POST destroy" do
render_views
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c5b034dc064..75e6b6f45a7 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -63,7 +63,7 @@ describe Projects::MergeRequestsController do
id: merge_request.iid,
format: format)
- expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s)
+ expect(response.body).to eq((merge_request.send(:"to_#{format}")).to_s)
end
it "should not escape Html" do
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
new file mode 100644
index 00000000000..d47e4ab9a4f
--- /dev/null
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -0,0 +1,49 @@
+require('spec_helper')
+
+describe Projects::ProjectMembersController do
+ let(:project) { create(:project) }
+ let(:another_project) { create(:project, :private) }
+ let(:user) { create(:user) }
+ let(:member) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ another_project.team << [member, :guest]
+ sign_in(user)
+ end
+
+ describe '#apply_import' do
+ shared_context 'import applied' do
+ before do
+ post(:apply_import, namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ source_project_id: another_project.id)
+ end
+ end
+
+ context 'when user can access source project members' do
+ before { another_project.team << [user, :guest] }
+ include_context 'import applied'
+
+ it 'imports source project members' do
+ expect(project.team_members).to include member
+ expect(response).to set_flash.to 'Successfully imported'
+ expect(response).to redirect_to(
+ namespace_project_project_members_path(project.namespace, project)
+ )
+ end
+ end
+
+ context 'when user is not member of a source project' do
+ include_context 'import applied'
+
+ it 'does not import team members' do
+ expect(project.team_members).to_not include member
+ end
+
+ it 'responds with not found' do
+ expect(response.status).to eq 404
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
new file mode 100644
index 00000000000..0f32a30f18b
--- /dev/null
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -0,0 +1,107 @@
+require 'spec_helper'
+
+describe Projects::SnippetsController do
+ let(:project) { create(:project_empty_repo, :public, snippets_enabled: true) }
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ project.team << [user2, :master]
+ end
+
+ describe 'GET #index' do
+ context 'when the project snippet is private' do
+ let!(:project_snippet) { create(:project_snippet, :private, project: project, author: user) }
+
+ context 'when anonymous' do
+ it 'does not include the private snippet' do
+ get :index, namespace_id: project.namespace.path, project_id: project.path
+
+ expect(assigns(:snippets)).not_to include(project_snippet)
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'when signed in as the author' do
+ before { sign_in(user) }
+
+ it 'renders the snippet' do
+ get :index, namespace_id: project.namespace.path, project_id: project.path
+
+ expect(assigns(:snippets)).to include(project_snippet)
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'when signed in as a project member' do
+ before { sign_in(user2) }
+
+ it 'renders the snippet' do
+ get :index, namespace_id: project.namespace.path, project_id: project.path
+
+ expect(assigns(:snippets)).to include(project_snippet)
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+ end
+
+ %w[show raw].each do |action|
+ describe "GET ##{action}" do
+ context 'when the project snippet is private' do
+ let(:project_snippet) { create(:project_snippet, :private, project: project, author: user) }
+
+ context 'when anonymous' do
+ it 'responds with status 404' do
+ get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when signed in as the author' do
+ before { sign_in(user) }
+
+ it 'renders the snippet' do
+ get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
+
+ expect(assigns(:snippet)).to eq(project_snippet)
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'when signed in as a project member' do
+ before { sign_in(user2) }
+
+ it 'renders the snippet' do
+ get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param
+
+ expect(assigns(:snippet)).to eq(project_snippet)
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context 'when the project snippet does not exist' do
+ context 'when anonymous' do
+ it 'responds with status 404' do
+ get action, namespace_id: project.namespace.path, project_id: project.path, id: 42
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when signed in' do
+ before { sign_in(user) }
+
+ it 'responds with status 404' do
+ get action, namespace_id: project.namespace.path, project_id: project.path, id: 42
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 1893e946f5c..069cd917e5a 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -83,6 +83,28 @@ describe ProjectsController do
end
end
+ describe "#update" do
+ render_views
+
+ let(:admin) { create(:admin) }
+
+ it "sets the repository to the right path after a rename" do
+ new_path = 'renamed_path'
+ project_params = { path: new_path }
+ controller.instance_variable_set(:@project, project)
+ sign_in(admin)
+
+ put :update,
+ namespace_id: project.namespace.to_param,
+ id: project.id,
+ project: project_params
+
+ expect(project.repository.path).to include(new_path)
+ expect(assigns(:repository).path).to eq(project.repository.path)
+ expect(response.status).to eq(200)
+ end
+ end
+
describe "#destroy" do
let(:admin) { create(:admin) }
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index 5a104ae7c99..b14d275f7fa 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -43,6 +43,28 @@ describe RootController do
end
end
+ context 'who has customized their dashboard setting for groups' do
+ before do
+ user.update_attribute(:dashboard, 'groups')
+ end
+
+ it 'redirects to their group list' do
+ get :index
+ expect(response).to redirect_to dashboard_groups_path
+ end
+ end
+
+ context 'who has customized their dashboard setting for todos' do
+ before do
+ user.update_attribute(:dashboard, 'todos')
+ end
+
+ it 'redirects to their todo list' do
+ get :index
+ expect(response).to redirect_to dashboard_todos_path
+ end
+ end
+
context 'who uses the default dashboard setting' do
it 'renders the default dashboard' do
get :index
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
new file mode 100644
index 00000000000..83cc8ec6d26
--- /dev/null
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -0,0 +1,101 @@
+require 'spec_helper'
+
+describe SessionsController do
+ describe '#create' do
+ before do
+ @request.env['devise.mapping'] = Devise.mappings[:user]
+ end
+
+ context 'when using standard authentications' do
+ context 'invalid password' do
+ it 'does not authenticate user' do
+ post(:create, user: { login: 'invalid', password: 'invalid' })
+
+ expect(response)
+ .to set_flash.now[:alert].to /Invalid login or password/
+ end
+ end
+
+ context 'when using valid password' do
+ let(:user) { create(:user) }
+
+ it 'authenticates user correctly' do
+ post(:create, user: { login: user.username, password: user.password })
+
+ expect(response).to set_flash.to /Signed in successfully/
+ expect(subject.current_user). to eq user
+ end
+ end
+ end
+
+ context 'when using two-factor authentication' do
+ let(:user) { create(:user, :two_factor) }
+
+ def authenticate_2fa(user_params)
+ post(:create, { user: user_params }, { otp_user_id: user.id })
+ end
+
+ ##
+ # See #14900 issue
+ #
+ context 'when authenticating with login and OTP of another user' do
+ context 'when another user has 2FA enabled' do
+ let(:another_user) { create(:user, :two_factor) }
+
+ context 'when OTP is valid for another user' do
+ it 'does not authenticate' do
+ authenticate_2fa(login: another_user.username,
+ otp_attempt: another_user.current_otp)
+
+ expect(subject.current_user).to_not eq another_user
+ end
+ end
+
+ context 'when OTP is invalid for another user' do
+ it 'does not authenticate' do
+ authenticate_2fa(login: another_user.username,
+ otp_attempt: 'invalid')
+
+ expect(subject.current_user).to_not eq another_user
+ end
+ end
+
+ context 'when authenticating with OTP' do
+ context 'when OTP is valid' do
+ it 'authenticates correctly' do
+ authenticate_2fa(otp_attempt: user.current_otp)
+
+ expect(subject.current_user).to eq user
+ end
+ end
+
+ context 'when OTP is invalid' do
+ before { authenticate_2fa(otp_attempt: 'invalid') }
+
+ it 'does not authenticate' do
+ expect(subject.current_user).to_not eq user
+ end
+
+ it 'warns about invalid OTP code' do
+ expect(response).to set_flash.now[:alert]
+ .to /Invalid two-factor code/
+ end
+ end
+ end
+
+ context 'when another user does not have 2FA enabled' do
+ let(:another_user) { create(:user) }
+
+ it 'does not leak that 2FA is disabled for another user' do
+ authenticate_2fa(login: another_user.username,
+ otp_attempt: 'invalid')
+
+ expect(response).to set_flash.now[:alert]
+ .to /Invalid two-factor code/
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/factories/file_uploader.rb b/spec/factories/file_uploader.rb
new file mode 100644
index 00000000000..1b36e21f2b0
--- /dev/null
+++ b/spec/factories/file_uploader.rb
@@ -0,0 +1,20 @@
+FactoryGirl.define do
+ factory :file_uploader do
+ project
+ secret nil
+
+ transient do
+ fixture { 'rails_sample.jpg' }
+ path { File.join(Rails.root, 'spec/fixtures', fixture) }
+ file { Rack::Test::UploadedFile.new(path) }
+ end
+
+ after(:build) do |uploader, evaluator|
+ uploader.store!(evaluator.file)
+ end
+
+ initialize_with do
+ new(project, secret)
+ end
+ end
+end
diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb
index 252bf2747e1..19a54946fe0 100644
--- a/spec/factories/forked_project_links.rb
+++ b/spec/factories/forked_project_links.rb
@@ -13,5 +13,10 @@ FactoryGirl.define do
factory :forked_project_link do
association :forked_to_project, factory: :project
association :forked_from_project, factory: :project
+
+ after(:create) do |link|
+ link.forked_from_project.reload
+ link.forked_to_project.reload
+ end
end
end
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 457859dedaf..62de081661d 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -1,9 +1,17 @@
require 'spec_helper'
-FactoryGirl.factories.map(&:name).each do |factory_name|
- describe "#{factory_name} factory" do
- it 'should be valid' do
- expect(build(factory_name)).to be_valid
+describe 'factories' do
+ FactoryGirl.factories.each do |factory|
+ describe "#{factory.name} factory" do
+ let(:entity) { build(factory.name) }
+
+ it 'does not raise error when created 'do
+ expect { entity }.to_not raise_error
+ end
+
+ it 'should be valid', if: factory.build_class < ActiveRecord::Base do
+ expect(entity).to be_valid
+ end
end
end
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index dc41be8246f..de6aed74fb4 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -61,7 +61,7 @@ describe "User Feed", feature: true do
end
it 'should have XHTML summaries in merge request descriptions' do
- expect(body).to match /Here is the fix: <img[^>]*\/>/
+ expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/
end
end
end
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
new file mode 100644
index 00000000000..39805da9d0b
--- /dev/null
+++ b/spec/features/dashboard_issues_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe "Dashboard Issues filtering", feature: true, js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:milestone) { create(:milestone, project: project) }
+
+ context 'filtering by milestone' do
+ before do
+ project.team << [user, :master]
+ login_as(user)
+
+ create(:issue, project: project, author: user, assignee: user)
+ create(:issue, project: project, author: user, assignee: user, milestone: milestone)
+
+ visit_issues
+ end
+
+ it 'should show all issues with no milestone' do
+ show_milestone_dropdown
+
+ click_link 'No Milestone'
+
+ expect(page).to have_selector('.issue', count: 1)
+ end
+
+ it 'should show all issues with any milestone' do
+ show_milestone_dropdown
+
+ click_link 'Any Milestone'
+
+ expect(page).to have_selector('.issue', count: 2)
+ end
+
+ it 'should show all issues with the selected milestone' do
+ show_milestone_dropdown
+
+ page.within '.dropdown-content' do
+ click_link milestone.title
+ end
+
+ expect(page).to have_selector('.issue', count: 1)
+ end
+ end
+
+ def show_milestone_dropdown
+ click_button 'Milestone'
+ expect(page).to have_selector('.dropdown-content', visible: true)
+ end
+
+ def visit_issues
+ visit issues_dashboard_path
+ end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
new file mode 100644
index 00000000000..41af789aae2
--- /dev/null
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -0,0 +1,64 @@
+require 'rails_helper'
+
+describe 'Awards Emoji', feature: true do
+ let!(:project) { create(:project) }
+ let!(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ describe 'Click award emoji from issue#show' do
+ let!(:issue) do
+ create(:issue,
+ author: @user,
+ assignee: @user,
+ project: project)
+ end
+
+ before do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'should increment the thumbsdown emoji', js: true do
+ find('[data-emoji="thumbsdown"]').click
+ sleep 2
+ expect(thumbsdown_emoji).to have_text("1")
+ end
+
+ context 'click the thumbsup emoji' do
+
+ it 'should increment the thumbsup emoji', js: true do
+ find('[data-emoji="thumbsup"]').click
+ sleep 2
+ expect(thumbsup_emoji).to have_text("1")
+ end
+
+ it 'should decrement the thumbsdown emoji', js: true do
+ expect(thumbsdown_emoji).to have_text("0")
+ end
+ end
+
+ context 'click the thumbsdown emoji' do
+
+ it 'should increment the thumbsdown emoji', js: true do
+ find('[data-emoji="thumbsdown"]').click
+ sleep 2
+ expect(thumbsdown_emoji).to have_text("1")
+ end
+
+ it 'should decrement the thumbsup emoji', js: true do
+ expect(thumbsup_emoji).to have_text("0")
+ end
+ end
+ end
+
+ def thumbsup_emoji
+ page.all('span.js-counter').first
+ end
+
+ def thumbsdown_emoji
+ page.all('span.js-counter').last
+ end
+end
diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb
index f6e33f651c4..99445185893 100644
--- a/spec/features/issues/filter_by_milestone_spec.rb
+++ b/spec/features/issues/filter_by_milestone_spec.rb
@@ -11,7 +11,41 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(Milestone::None.title)
- expect(page).to have_css('.issue .title', count: 1)
+ expect(page).to have_css('.issue', count: 1)
+ end
+
+ context 'filters by upcoming milestone', js: true do
+ it 'should not show issues with no expiry' do
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 0)
+ end
+
+ it 'should show issues in future' do
+ milestone = create(:milestone, project: project, due_date: Date.tomorrow)
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 1)
+ end
+
+ it 'should not show issues in past' do
+ milestone = create(:milestone, project: project, due_date: Date.yesterday)
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 0)
+ end
end
scenario 'filters by a specific Milestone', js: true do
@@ -21,7 +55,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(milestone.title)
- expect(page).to have_css('.issue .title', count: 1)
+ expect(page).to have_css('.issue', count: 1)
end
def visit_issues(project)
@@ -30,8 +64,6 @@ feature 'Issue filtering by Milestone', feature: true do
def filter_by_milestone(title)
find(".js-milestone-select").click
- sleep 0.5
- find(".milestone-filter a", text: title).click
- sleep 1
+ find(".milestone-filter .dropdown-content a", text: title).click
end
end
diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb
new file mode 100644
index 00000000000..90822a8c123
--- /dev/null
+++ b/spec/features/issues/filter_issues_spec.rb
@@ -0,0 +1,119 @@
+require 'rails_helper'
+
+describe 'Filter issues', feature: true do
+
+ let!(:project) { create(:project) }
+ let!(:user) { create(:user)}
+ let!(:milestone) { create(:milestone, project: project) }
+ let!(:label) { create(:label, project: project) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ describe 'Filter issues for assignee from issues#index' do
+
+ before do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('.js-assignee-search').click
+
+ find('.dropdown-menu-user-link', text: user.username).click
+
+ sleep 2
+ end
+
+ context 'assignee', js: true do
+ it 'should update to current user' do
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ end
+
+ it 'should not change when closed link is clicked' do
+ find('.issues-state-filters a', text: "Closed").click
+
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ end
+
+
+ it 'should not change when all link is clicked' do
+ find('.issues-state-filters a', text: "All").click
+
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ end
+ end
+ end
+
+ describe 'Filter issues for milestone from issues#index' do
+
+ before do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('.js-milestone-select').click
+
+ find('.milestone-filter .dropdown-content a', text: milestone.title).click
+
+ sleep 2
+ end
+
+ context 'milestone', js: true do
+ it 'should update to current milestone' do
+ expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
+ end
+
+ it 'should not change when closed link is clicked' do
+ find('.issues-state-filters a', text: "Closed").click
+
+ expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
+ end
+
+
+ it 'should not change when all link is clicked' do
+ find('.issues-state-filters a', text: "All").click
+
+ expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title)
+ end
+ end
+ end
+
+ describe 'Filter issues for assignee and label from issues#index' do
+
+ before do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('.js-assignee-search').click
+
+ find('.dropdown-menu-user-link', text: user.username).click
+
+ sleep 2
+
+ find('.js-label-select').click
+
+ find('.dropdown-menu-labels .dropdown-content a', text: label.title).click
+
+ sleep 2
+ end
+
+ context 'assignee and label', js: true do
+ it 'should update to current assignee and label' do
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
+ end
+
+ it 'should not change when closed link is clicked' do
+ find('.issues-state-filters a', text: "Closed").click
+
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
+ end
+
+
+ it 'should not change when all link is clicked' do
+ find('.issues-state-filters a', text: "All").click
+
+ expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name)
+ expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title)
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
new file mode 100644
index 00000000000..3eb903a93fe
--- /dev/null
+++ b/spec/features/issues/update_issues_spec.rb
@@ -0,0 +1,117 @@
+require 'rails_helper'
+
+feature 'Multiple issue updating from issues#index', feature: true do
+ let!(:project) { create(:project) }
+ let!(:issue) { create(:issue, project: project) }
+ let!(:user) { create(:user)}
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ context 'status', js: true do
+ it 'should be set to closed' do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('#check_all_issues').click
+ find('.js-issue-status').click
+
+ find('.dropdown-menu-status a', text: 'Closed').click
+ click_update_issues_button
+ expect(page).to have_selector('.issue', count: 0)
+ end
+
+ it 'should be set to open' do
+ create_closed
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('.issues-state-filters a', text: 'Closed').click
+
+ find('#check_all_issues').click
+ find('.js-issue-status').click
+
+ find('.dropdown-menu-status a', text: 'Open').click
+ click_update_issues_button
+ expect(page).to have_selector('.issue', count: 0)
+ end
+ end
+
+ context 'assignee', js: true do
+ it 'should update to current user' do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('#check_all_issues').click
+ find('.js-update-assignee').click
+
+ find('.dropdown-menu-user-link', text: user.username).click
+ click_update_issues_button
+
+ page.within('.issue .controls') do
+ expect(find('.author_link')["data-original-title"]).to have_content(user.name)
+ end
+ end
+
+ it 'should update to unassigned' do
+ create_assigned
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('#check_all_issues').click
+ find('.js-update-assignee').click
+
+ click_link 'Unassigned'
+ click_update_issues_button
+
+ within first('.issue .controls') do
+ expect(page).to have_no_selector('.author_link')
+ end
+ end
+ end
+
+ context 'milestone', js: true do
+ let(:milestone) { create(:milestone, project: project) }
+
+ it 'should update milestone' do
+ visit namespace_project_issues_path(project.namespace, project)
+
+ find('#check_all_issues').click
+ find('.issues_bulk_update .js-milestone-select').click
+
+ find('.dropdown-menu-milestone a', text: milestone.title).click
+ click_update_issues_button
+
+ expect(find('.issue')).to have_content milestone.title
+ end
+
+ it 'should set to no milestone' do
+ create_with_milestone
+ visit namespace_project_issues_path(project.namespace, project)
+
+ expect(first('.issue')).to have_content milestone.title
+
+ find('#check_all_issues').click
+ find('.issues_bulk_update .js-milestone-select').click
+
+ find('.dropdown-menu-milestone a', text: "No Milestone").click
+ click_update_issues_button
+
+ expect(first('.issue')).to_not have_content milestone.title
+ end
+ end
+
+ def create_closed
+ create(:issue, project: project, state: :closed)
+ end
+
+ def create_assigned
+ create(:issue, project: project, assignee: user)
+ end
+
+ def create_with_milestone
+ create(:issue, project: project, milestone: milestone)
+ end
+
+ def click_update_issues_button
+ find('.update_selected_issues').click
+ end
+end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index e844e681ebf..79000666ccc 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -22,7 +22,7 @@ describe 'Issues', feature: true do
before do
visit edit_namespace_project_issue_path(project.namespace, project, issue)
- click_link "Edit"
+ click_button "Go full screen"
end
it 'should open new issue popup' do
@@ -34,20 +34,7 @@ describe 'Issues', feature: true do
fill_in 'issue_title', with: 'bug 345'
fill_in 'issue_description', with: 'bug description'
end
-
- it 'does not change issue count' do
- expect { click_button 'Save changes' }.to_not change { Issue.count }
- end
-
- it 'should update issue fields' do
- click_button 'Save changes'
-
- expect(page).to have_content @user.name
- expect(page).to have_content 'bug 345'
- expect(page).to have_content project.name
- end
end
-
end
describe 'Editing issue assignee' do
@@ -70,7 +57,7 @@ describe 'Issues', feature: true do
click_button 'Save changes'
page.within('.assignee') do
- expect(page).to have_content 'None'
+ expect(page).to have_content 'No assignee - assign yourself'
end
expect(issue.reload.assignee).to be_nil
@@ -198,20 +185,26 @@ describe 'Issues', feature: true do
end
describe 'update assignee from issue#show' do
- let(:issue) { create(:issue, project: project, author: @user) }
+ let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
context 'by autorized user' do
- it 'with dropdown menu' do
+ it 'allows user to select unassigned', js: true do
visit namespace_project_issue_path(project.namespace, project, issue)
- find('.issuable-sidebar #issue_assignee_id').
- set project.team.members.first.id
- click_button 'Update Issue'
+ page.within('.assignee') do
+ expect(page).to have_content "#{@user.name}"
+ end
+
+ find('.block.assignee .edit-link').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.dropdown-menu-user-link').click
+ sleep 2
+ page.within('.assignee') do
+ expect(page).to have_content 'No assignee'
+ end
- expect(page).to have_content 'Assignee'
- has_select?('issue_assignee_id',
- selected: project.team.members.first.name)
+ expect(issue.reload.assignee).to be_nil
end
end
@@ -221,8 +214,6 @@ describe 'Issues', feature: true do
before :each do
project.team << [[guest], :guest]
- issue.assignee = @user
- issue.save
end
it 'shows assignee text', js: true do
@@ -241,20 +232,23 @@ describe 'Issues', feature: true do
context 'by authorized user' do
- it 'with dropdown menu' do
- visit namespace_project_issue_path(project.namespace, project, issue)
- find('.issuable-sidebar').
- select(milestone.title, from: 'issue_milestone_id')
- click_button 'Update Issue'
+ it 'allows user to select unassigned', js: true do
+ visit namespace_project_issue_path(project.namespace, project, issue)
- expect(page).to have_content "Milestone changed to #{milestone.title}"
+ page.within('.milestone') do
+ expect(page).to have_content "None"
+ end
+ find('.block.milestone .edit-link').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.dropdown-content li').click
+ sleep 2
page.within('.milestone') do
- expect(page).to have_content milestone.title
+ expect(page).to have_content 'None'
end
- has_select?('issue_assignee_id', selected: milestone.title)
+ expect(issue.reload.milestone).to be_nil
end
end
@@ -283,25 +277,6 @@ describe 'Issues', feature: true do
issue.assignee = user2
issue.save
end
-
- it 'allows user to remove assignee', js: true do
- visit namespace_project_issue_path(project.namespace, project, issue)
-
- page.within('.assignee') do
- expect(page).to have_content user2.name
- end
-
- find('.assignee .edit-link').click
- sleep 2 # wait for ajax stuff to complete
- first('.user-result').click
-
- page.within('.assignee') do
- expect(page).to have_content 'None'
- end
-
- sleep 2 # wait for ajax stuff to complete
- expect(issue.reload.assignee).to be_nil
- end
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 12fd8d37210..3d0d0e59fd7 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -39,7 +39,7 @@ describe 'GitLab Markdown', feature: true do
end
def doc(html = @html)
- Nokogiri::HTML::DocumentFragment.parse(html)
+ @doc ||= Nokogiri::HTML::DocumentFragment.parse(html)
end
# Shared behavior that all pipelines should exhibit
@@ -230,6 +230,7 @@ describe 'GitLab Markdown', feature: true do
file = Gollum::File.new(@project_wiki.wiki)
expect(file).to receive(:path).and_return('images/example.jpg')
expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file)
+ allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' }
@html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki })
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
new file mode 100644
index 00000000000..00b60bd0e75
--- /dev/null
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+feature 'Create New Merge Request', feature: true, js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ before do
+ project.team << [user, :master]
+
+ login_as user
+ visit namespace_project_merge_requests_path(project.namespace, project)
+ end
+
+ it 'generates a diff for an orphaned branch' do
+ click_link 'New Merge Request'
+
+ first('.js-source-branch').click
+ first('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch').click
+
+ click_button "Compare branches"
+ click_link "Changes"
+
+ expect(page).to have_content "README.md"
+ expect(page).to have_content "wm.png"
+
+ fill_in "merge_request_title", with: "Orphaned MR test"
+ click_button "Submit merge request"
+
+ click_link "Check out branch"
+
+ expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch'
+ end
+end
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index b76e4c74c79..c57ab5f3b03 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -11,7 +11,41 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project)
filter_by_milestone(Milestone::None.title)
- expect(page).to have_css('.merge-request-title', count: 1)
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ context 'filters by upcoming milestone', js: true do
+ it 'should not show issues with no expiry' do
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 0)
+ end
+
+ it 'should show issues in future' do
+ milestone = create(:milestone, project: project, due_date: Date.tomorrow)
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ it 'should not show issues in past' do
+ milestone = create(:milestone, project: project, due_date: Date.yesterday)
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 0)
+ end
end
scenario 'filters by a specific Milestone', js: true do
@@ -21,7 +55,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project)
filter_by_milestone(milestone.title)
- expect(page).to have_css('.merge-request-title', count: 1)
+ expect(page).to have_css('.merge-request', count: 1)
end
def visit_merge_requests(project)
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index d9a8058efd9..5f855ccc701 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -152,7 +152,7 @@ describe 'Comments', feature: true do
it 'has .new_note css class' do
page.within('.js-temp-notes-holder') do
- expect(subject).to have_css('.new_note')
+ expect(subject).to have_css('.new-note')
end
end
end
@@ -210,7 +210,7 @@ describe 'Comments', feature: true do
is_expected.to have_content('Another comment on line 10')
is_expected.to have_css('.notes_holder')
is_expected.to have_css('.notes_holder .note', count: 1)
- is_expected.to have_button('Reply')
+ is_expected.to have_button('Reply...')
end
end
end
@@ -225,6 +225,6 @@ describe 'Comments', feature: true do
end
def click_diff_line(data = line_code)
- page.find(%Q{button[data-line-code="#{data}"]}, visible: false).click
+ execute_script("$('button[data-line-code=\"#{data}\"]').click()")
end
end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
new file mode 100644
index 00000000000..13c9b95b316
--- /dev/null
+++ b/spec/features/projects/badges/list_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+feature 'list of badges' do
+ include Select2Helper
+
+ background do
+ user = create(:user)
+ project = create(:project)
+ project.team << [user, :master]
+ login_as(user)
+ visit edit_namespace_project_path(project.namespace, project)
+ end
+
+ scenario 'user displays list of badges' do
+ click_link 'Badges'
+
+ expect(page).to have_content 'build status'
+ expect(page).to have_content 'Markdown'
+ expect(page).to have_content 'HTML'
+ expect(page).to have_css('.highlight', count: 2)
+ expect(page).to have_xpath("//img[@alt='build status']")
+
+ page.within('.highlight', match: :first) do
+ expect(page).to have_content 'badges/master/build.svg'
+ end
+ end
+
+ scenario 'user changes current ref on badges list page', js: true do
+ click_link 'Badges'
+ select2('improve/awesome', from: '#ref')
+
+ expect(page).to have_content 'badges/improve/awesome/build.svg'
+ end
+end
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 84c036e59c0..3e6289a46b1 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -1,19 +1,46 @@
require 'spec_helper'
describe "Search", feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
before do
- login_as :user
- @project = create(:project, namespace: @user.namespace)
- @project.team << [@user, :reporter]
+ login_with(user)
+ project.team << [user, :reporter]
visit search_path
+ end
- page.within '.search-holder' do
- fill_in "search", with: @project.name[0..3]
- click_button "Search"
+ describe 'searching for Projects' do
+ it 'finds a project' do
+ page.within '.search-holder' do
+ fill_in "search", with: project.name[0..3]
+ click_button "Search"
+ end
+
+ expect(page).to have_content project.name
end
end
- it "should show project in search results" do
- expect(page).to have_content @project.name
+ context 'search for comments' do
+ it 'finds a snippet' do
+ snippet = create(:project_snippet, :private, project: project, author: user, title: 'Some title')
+ note = create(:note,
+ noteable: snippet,
+ author: user,
+ note: 'Supercalifragilisticexpialidocious',
+ project: project)
+ # Must visit project dashboard since global search won't search
+ # everything (e.g. comments, snippets, etc.)
+ visit namespace_project_path(project.namespace, project)
+
+ page.within '.search' do
+ fill_in 'search', with: note.note
+ click_button 'Go'
+ end
+
+ click_link 'Comments'
+
+ expect(page).to have_link(snippet.title)
+ end
end
end
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
new file mode 100644
index 00000000000..db53a9cec97
--- /dev/null
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -0,0 +1,78 @@
+require 'spec_helper'
+
+describe "Internal Project Snippets Access", feature: true do
+ include AccessMatchers
+
+ let(:project) { create(:project, :internal) }
+
+ let(:owner) { project.owner }
+ let(:master) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:reporter) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) }
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
+
+ before do
+ project.team << [master, :master]
+ project.team << [developer, :developer]
+ project.team << [reporter, :reporter]
+ project.team << [guest, :guest]
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { namespace_project_snippets_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/new" do
+ subject { new_namespace_project_snippet_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for an internal snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for a private snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
new file mode 100644
index 00000000000..d23d645c8e5
--- /dev/null
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe "Private Project Snippets Access", feature: true do
+ include AccessMatchers
+
+ let(:project) { create(:project, :private) }
+
+ let(:owner) { project.owner }
+ let(:master) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:reporter) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
+
+ before do
+ project.team << [master, :master]
+ project.team << [developer, :developer]
+ project.team << [reporter, :reporter]
+ project.team << [guest, :guest]
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { namespace_project_snippets_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/new" do
+ subject { new_namespace_project_snippet_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for a private snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
new file mode 100644
index 00000000000..e3665b6116a
--- /dev/null
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -0,0 +1,93 @@
+require 'spec_helper'
+
+describe "Public Project Snippets Access", feature: true do
+ include AccessMatchers
+
+ let(:project) { create(:project, :public) }
+
+ let(:owner) { project.owner }
+ let(:master) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:reporter) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:public_snippet) { create(:project_snippet, :public, project: project, author: owner) }
+ let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) }
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
+
+ before do
+ project.team << [master, :master]
+ project.team << [developer, :developer]
+ project.team << [reporter, :reporter]
+ project.team << [guest, :guest]
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { namespace_project_snippets_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_allowed_for :external }
+ it { is_expected.to be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/new" do
+ subject { new_namespace_project_snippet_path(project.namespace, project) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for a public snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, public_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_allowed_for :external }
+ it { is_expected.to be_allowed_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for an internal snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/:id for a private snippet" do
+ subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for owner }
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for developer }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_denied_for :visitor }
+ end
+end
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
new file mode 100644
index 00000000000..39d5cefbc2a
--- /dev/null
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
@@ -0,0 +1,42 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: reply@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+In-Reply-To: <issue_1@localhost>
+References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+I could not disagree more. I am obviously biased but adventure time is the
+greatest show ever created. Everyone should watch it.
+
+- Jake out
+
+
+On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
+<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote:
+>
+>
+>
+> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
+>
+> ---
+> hey guys everyone knows adventure time sucks!
+>
+> ---
+> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
+>
+> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
+>
diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml
index 1e696389954..980e10a8812 100644
--- a/spec/fixtures/emails/valid_reply.eml
+++ b/spec/fixtures/emails/valid_reply.eml
@@ -7,6 +7,8 @@ Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+In-Reply-To: <issue_1@localhost>
+References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
@@ -37,4 +39,4 @@ On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
>
> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
-> \ No newline at end of file
+>
diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb
new file mode 100644
index 00000000000..b20373a96fb
--- /dev/null
+++ b/spec/helpers/form_helper_spec.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+
+describe FormHelper do
+ describe 'form_errors' do
+ it 'returns nil when model has no errors' do
+ model = double(errors: [])
+
+ expect(helper.form_errors(model)).to be_nil
+ end
+
+ it 'renders an alert div' do
+ model = double(errors: errors_stub('Error 1'))
+
+ expect(helper.form_errors(model)).
+ to include('<div class="alert alert-danger" id="error_explanation">')
+ end
+
+ it 'contains a summary message' do
+ single_error = double(errors: errors_stub('A'))
+ multi_errors = double(errors: errors_stub('A', 'B', 'C'))
+
+ expect(helper.form_errors(single_error)).
+ to include('<h4>The form contains the following error:')
+ expect(helper.form_errors(multi_errors)).
+ to include('<h4>The form contains the following errors:')
+ end
+
+ it 'renders each message' do
+ model = double(errors: errors_stub('Error 1', 'Error 2', 'Error 3'))
+
+ errors = helper.form_errors(model)
+
+ aggregate_failures do
+ expect(errors).to include('<li>Error 1</li>')
+ expect(errors).to include('<li>Error 2</li>')
+ expect(errors).to include('<li>Error 3</li>')
+ end
+ end
+
+ def errors_stub(*messages)
+ ActiveModel::Errors.new(double).tap do |errors|
+ messages.each { |msg| errors.add(:base, msg) }
+ end
+ end
+ end
+end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 9adcd916ced..13de88e2f21 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -150,13 +150,6 @@ describe GitlabMarkdownHelper do
end
end
- describe 'random_markdown_tip' do
- it 'returns a random Markdown tip' do
- stub_const("#{described_class}::MARKDOWN_TIPS", ['Random tip'])
- expect(random_markdown_tip).to eq 'Random tip'
- end
- end
-
describe '#first_line_in_markdown' do
let(:text) { "@#{user.username}, can you look at this?\nHello world\n"}
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index ffd8ebae029..543593cf389 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -80,7 +80,7 @@ describe IssuesHelper do
end
end
- describe '#url_for_new_issue' do
+ describe 'url_for_new_issue' do
let(:issues_url) { ext_project.external_issue_tracker.new_issue_url }
let(:ext_expected) do
issues_url.gsub(':project_id', ext_project.id.to_s)
@@ -117,7 +117,7 @@ describe IssuesHelper do
end
end
- describe "#merge_requests_sentence" do
+ describe "merge_requests_sentence" do
subject { merge_requests_sentence(merge_requests)}
let(:merge_requests) do
[ build(:merge_request, iid: 1), build(:merge_request, iid: 2),
@@ -127,7 +127,7 @@ describe IssuesHelper do
it { is_expected.to eq("!1, !2, or !3") }
end
- describe "#note_active_class" do
+ describe "note_active_class" do
before do
@note = create :note
@note1 = create :note
@@ -142,10 +142,25 @@ describe IssuesHelper do
end
end
- describe "#awards_sort" do
+ describe "awards_sort" do
it "sorts a hash so thumbsup and thumbsdown are always on top" do
data = { "thumbsdown" => "some value", "lifter" => "some value", "thumbsup" => "some value" }
expect(awards_sort(data).keys).to eq(["thumbsup", "thumbsdown", "lifter"])
end
end
+
+ describe "milestone_options" do
+ it "gets closed milestone from current issue" do
+ closed_milestone = create(:closed_milestone, project: project)
+ milestone1 = create(:milestone, project: project)
+ milestone2 = create(:milestone, project: project)
+ issue.update_attributes(milestone_id: closed_milestone.id)
+
+ options = milestone_options(issue)
+
+ expect(options).to have_selector('option[selected]', text: closed_milestone.title)
+ expect(options).to have_selector('option', text: milestone1.title)
+ expect(options).to have_selector('option', text: milestone2.title)
+ end
+ end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index e5df59c4fba..2f9291afc3f 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -19,7 +19,9 @@ describe PreferencesHelper do
['Your Projects (default)', 'projects'],
['Starred Projects', 'stars'],
["Your Projects' Activity", 'project_activity'],
- ["Starred Projects' Activity", 'starred_project_activity']
+ ["Starred Projects' Activity", 'starred_project_activity'],
+ ["Your Groups", 'groups'],
+ ["Your Todos", 'todos']
]
end
end
diff --git a/spec/javascripts/fixtures/zen_mode.html.haml b/spec/javascripts/fixtures/zen_mode.html.haml
index 1701652c61e..cb906a7feaa 100644
--- a/spec/javascripts/fixtures/zen_mode.html.haml
+++ b/spec/javascripts/fixtures/zen_mode.html.haml
@@ -1,4 +1,4 @@
-.zennable
+.md-area
.zen-backdrop
%textarea#note_note.js-gfm-input.markdown-area
%a.js-zen-enter(tabindex="-1" href="#")
diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee
index 86ba9dd8e96..ea27f36e9b5 100644
--- a/spec/javascripts/issue_spec.js.coffee
+++ b/spec/javascripts/issue_spec.js.coffee
@@ -29,8 +29,8 @@ describe 'reopen/close issue', ->
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/close')
- req.success saved: true
-
+ req.success id: 34
+
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
expect($btnReopen).toBeHidden()
@@ -94,7 +94,7 @@ describe 'reopen/close issue', ->
spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/reopen')
- req.success saved: true
+ req.success id: 34
$btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen')
diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb
new file mode 100644
index 00000000000..330678f7f16
--- /dev/null
+++ b/spec/lib/award_emoji_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe AwardEmoji do
+ describe '.urls' do
+ subject { AwardEmoji.urls }
+
+ it { is_expected.to be_an_instance_of(Array) }
+ it { is_expected.to_not be_empty }
+
+ context 'every Hash in the Array' do
+ it 'has the correct keys and values' do
+ subject.each do |hash|
+ expect(hash[:name]).to be_an_instance_of(String)
+ expect(hash[:path]).to be_an_instance_of(String)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
index 5e23c5c319a..fe2ce092e6b 100644
--- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
+++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
@@ -70,20 +70,22 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do
end
context 'linking internal resources' do
- it "the created link's text will be equal to the resource's text" do
+ it "the created link's text includes the resource's text and wiki base path" do
tag = '[[wiki-slug]]'
doc = filter("See #{tag}", project_wiki: project_wiki)
+ expected_path = ::File.join(project_wiki.wiki_base_path, 'wiki-slug')
expect(doc.at_css('a').text).to eq 'wiki-slug'
- expect(doc.at_css('a')['href']).to eq 'wiki-slug'
+ expect(doc.at_css('a')['href']).to eq expected_path
end
it "the created link's text will be link-text" do
tag = '[[link-text|wiki-slug]]'
doc = filter("See #{tag}", project_wiki: project_wiki)
+ expected_path = ::File.join(project_wiki.wiki_base_path, 'wiki-slug')
expect(doc.at_css('a').text).to eq 'link-text'
- expect(doc.at_css('a')['href']).to eq 'wiki-slug'
+ expect(doc.at_css('a')['href']).to eq expected_path
end
end
diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb
new file mode 100644
index 00000000000..dd5594750c8
--- /dev/null
+++ b/spec/lib/banzai/filter/image_link_filter_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Banzai::Filter::ImageLinkFilter, lib: true do
+ include FilterSpecHelper
+
+ def image(path)
+ %(<img src="#{path}" />)
+ end
+
+ it 'wraps the image with a link to the image src' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href']
+ end
+
+ it 'does not wrap a duplicate link' do
+ exp = act = %q(<a href="/whatever">#{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')}</a>)
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'works with external images' do
+ doc = filter(image('https://i.imgur.com/DfssX9C.jpg'))
+ expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href']
+ end
+end
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 5a0d3d577a8..266ebef33d6 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -95,6 +95,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue]
end
+
+ it 'does not process links containing issue numbers followed by text' do
+ href = "#{reference}st"
+ doc = reference_filter("<a href='#{href}'></a>")
+ link = doc.css('a').first.attr('href')
+
+ expect(link).to eq(href)
+ end
end
context 'cross-project reference' do
diff --git a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
index 3e25406e498..7aa1b4a3bf6 100644
--- a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
@@ -11,7 +11,7 @@ describe Banzai::Pipeline::WikiPipeline do
Foo
MD
- result = described_class.call(markdown, project: spy, project_wiki: double)
+ result = described_class.call(markdown, project: spy, project_wiki: spy)
aggregate_failures do
expect(result[:output].text).not_to include '[['
@@ -29,7 +29,7 @@ describe Banzai::Pipeline::WikiPipeline do
Foo
MD
- output = described_class.to_html(markdown, project: spy, project_wiki: double)
+ output = described_class.to_html(markdown, project: spy, project_wiki: spy)
expect(output).to include('[[<em>toc</em>]]')
end
@@ -42,7 +42,7 @@ describe Banzai::Pipeline::WikiPipeline do
Foo
MD
- output = described_class.to_html(markdown, project: spy, project_wiki: double)
+ output = described_class.to_html(markdown, project: spy, project_wiki: spy)
aggregate_failures do
expect(output).not_to include('<ul>')
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index f38fadda9ba..566035c60d0 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe ExtractsPath, lib: true do
include ExtractsPath
include RepoHelpers
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
let(:project) { double('project') }
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
new file mode 100644
index 00000000000..329792bb685
--- /dev/null
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build do
+ let(:project) { create(:project) }
+ let(:sha) { project.commit.sha }
+ let(:branch) { 'master' }
+ let(:badge) { described_class.new(project, branch) }
+
+ describe '#type' do
+ subject { badge.type }
+ it { is_expected.to eq 'image/svg+xml' }
+ end
+
+ describe '#to_html' do
+ let(:html) { Nokogiri::HTML.parse(badge.to_html) }
+ let(:a_href) { html.at('a') }
+
+ it 'points to link' do
+ expect(a_href[:href]).to eq badge.link_url
+ end
+
+ it 'contains clickable image' do
+ expect(a_href.children.first.name).to eq 'img'
+ end
+ end
+
+ describe '#to_markdown' do
+ subject { badge.to_markdown }
+
+ it { is_expected.to include badge.image_url }
+ it { is_expected.to include badge.link_url }
+ end
+
+ describe '#image_url' do
+ subject { badge.image_url }
+ it { is_expected.to include "badges/#{branch}/build.svg" }
+ end
+
+ describe '#link_url' do
+ subject { badge.link_url }
+ it { is_expected.to include "commits/#{branch}" }
+ end
+
+ context 'build exists' do
+ let(:ci_commit) { create(:ci_commit, project: project, sha: sha) }
+ let!(:build) { create(:ci_build, commit: ci_commit) }
+
+
+ context 'build success' do
+ before { build.success! }
+
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-success' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about success' do
+ expect(status_node(data, 'success')).to be_truthy
+ end
+ end
+ end
+
+ context 'build failed' do
+ before { build.drop! }
+
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-failed' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about failure' do
+ expect(status_node(data, 'failed')).to be_truthy
+ end
+ end
+ end
+ end
+
+ context 'build does not exist' do
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-unknown' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about unknown build' do
+ expect(status_node(data, 'unknown')).to be_truthy
+ end
+ end
+ end
+
+ def status_node(data, status)
+ xml = Nokogiri::XML.parse(data)
+ xml.at(%Q{text:contains("#{status}")})
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 844fd79c991..e9b8ce6b5bb 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -23,11 +23,21 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
end
it do
+ message = "Awesome commit (Closes: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Awesome commit (closes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Awesome commit (closes: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Closed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
@@ -38,105 +48,210 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
end
it do
+ message = "closed: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Closing: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "closing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "closing: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Close: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "close #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "close: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Awesome commit (Fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Awesome commit (Fixes: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Awesome commit (fixes #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Awesome commit (Fixes: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Fixed: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "fixed #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "fixed: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Fixing: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "fixing #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "fixing: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Fix: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "fix #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "fix: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Awesome commit (Resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Awesome commit (Resolves: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Awesome commit (resolves #{reference})"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Awesome commit (resolves: #{reference})"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Resolved: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "resolved #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "resolved: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Resolving: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "resolving #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "resolving: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "Resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
it do
+ message = "Resolve: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
+ it do
message = "resolve #{reference}"
expect(subject.closed_by_message(message)).to eq([issue])
end
+ it do
+ message = "resolve: #{reference}"
+ expect(subject.closed_by_message(message)).to eq([issue])
+ end
+
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
@@ -236,6 +351,6 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
end
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
end
diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb
index abe179cd4af..36267faeb93 100644
--- a/spec/lib/gitlab/email/receiver_spec.rb
+++ b/spec/lib/gitlab/email/receiver_spec.rb
@@ -3,6 +3,7 @@ require "spec_helper"
describe Gitlab::Email::Receiver, lib: true do
before do
stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
+ stub_config_setting(host: 'localhost')
end
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
@@ -137,5 +138,27 @@ describe Gitlab::Email::Receiver, lib: true do
expect(note.note).to include(markdown)
end
+
+ context 'when sub-addressing is not supported' do
+ before do
+ stub_incoming_email_setting(enabled: true, address: nil)
+ end
+
+ shared_examples 'an email that contains a reply key' do |header|
+ it "fetches the reply key from the #{header} header and creates a comment" do
+ expect { receiver.execute }.to change { noteable.notes.count }.by(1)
+ note = noteable.notes.last
+
+ expect(note.author).to eq(sent_notification.recipient)
+ expect(note.note).to include('I could not disagree more.')
+ end
+ end
+
+ context 'reply key is in the References header' do
+ let(:email_raw) { fixture_file('emails/reply_without_subaddressing_and_key_inside_references.eml') }
+
+ it_behaves_like 'an email that contains a reply key', 'References'
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb
new file mode 100644
index 00000000000..2dc71be0254
--- /dev/null
+++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::FogbugzImport::Client, lib: true do
+
+ let(:client) { described_class.new(uri: '', token: '') }
+ let(:one_user) { { 'people' => { 'person' => { "ixPerson" => "2", "sFullName" => "James" } } } }
+ let(:two_users) { { 'people' => { 'person' => [one_user, { "ixPerson" => "3" }] } } }
+
+ it 'retrieves user_map with one user' do
+ stub_api(one_user)
+
+ expect(client.user_map.count).to eq(1)
+ end
+
+ it 'retrieves user_map with two users' do
+ stub_api(two_users)
+
+ expect(client.user_map.count).to eq(2)
+ end
+
+ def stub_api(users)
+ allow_any_instance_of(::Fogbugz::Interface).to receive(:command).with(:listPeople).and_return(users)
+ end
+end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
new file mode 100644
index 00000000000..eda956e6f0a
--- /dev/null
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+describe Gitlab::Gfm::UploadsRewriter do
+ let(:user) { create(:user) }
+ let(:old_project) { create(:project) }
+ let(:new_project) { create(:project) }
+ let(:rewriter) { described_class.new(text, old_project, user) }
+
+ context 'text contains links to uploads' do
+ let(:image_uploader) do
+ build(:file_uploader, project: old_project)
+ end
+
+ let(:zip_uploader) do
+ build(:file_uploader, project: old_project,
+ fixture: 'ci_build_artifacts.zip')
+ end
+
+ let(:text) do
+ "Text and #{image_uploader.to_markdown} and #{zip_uploader.to_markdown}"
+ end
+
+ describe '#rewrite' do
+ let!(:new_text) { rewriter.rewrite(new_project) }
+
+ let(:old_files) { [image_uploader, zip_uploader].map(&:file) }
+ let(:new_files) do
+ described_class.new(new_text, new_project, user).files
+ end
+
+ let(:old_paths) { old_files.map(&:path) }
+ let(:new_paths) { new_files.map(&:path) }
+
+ it 'rewrites content' do
+ expect(new_text).to_not eq text
+ expect(new_text.length).to eq text.length
+ end
+
+ it 'copies files' do
+ expect(new_files).to all(exist)
+ expect(old_paths).to_not match_array new_paths
+ expect(old_paths).to all(include(old_project.path_with_namespace))
+ expect(new_paths).to all(include(new_project.path_with_namespace))
+ end
+
+ it 'does not remove old files' do
+ expect(old_files).to all(exist)
+ end
+
+ it 'generates a new secret for each file' do
+ expect(new_paths).to_not include image_uploader.secret
+ expect(new_paths).to_not include zip_uploader.secret
+ end
+ end
+
+ describe '#needs_rewrite?' do
+ subject { rewriter.needs_rewrite? }
+ it { is_expected.to eq true }
+ end
+
+ describe '#files' do
+ subject { rewriter.files }
+ it { is_expected.to be_an(Array) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index bcdba8d4c12..afb3e26f8fb 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -7,24 +7,8 @@ describe Gitlab::IncomingEmail, lib: true do
stub_incoming_email_setting(enabled: true)
end
- context "when the address is valid" do
- before do
- stub_incoming_email_setting(address: "replies+%{key}@example.com")
- end
-
- it "returns true" do
- expect(described_class.enabled?).to be_truthy
- end
- end
-
- context "when the address is invalid" do
- before do
- stub_incoming_email_setting(address: "replies@example.com")
- end
-
- it "returns false" do
- expect(described_class.enabled?).to be_falsey
- end
+ it 'returns true' do
+ expect(described_class.enabled?).to be_truthy
end
end
@@ -58,4 +42,10 @@ describe Gitlab::IncomingEmail, lib: true do
expect(described_class.key_from_address("replies+key@example.com")).to eq("key")
end
end
+
+ context 'self.key_from_fallback_reply_message_id' do
+ it 'returns reply key' do
+ expect(described_class.key_from_fallback_reply_message_id('reply-key@localhost')).to eq('key')
+ end
+ end
end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index 32a19bf344b..f5b66b8156f 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -33,7 +33,7 @@ describe Gitlab::LDAP::Access, lib: true do
it { is_expected.to be_falsey }
- it 'should block user in GitLab' do
+ it 'blocks user in GitLab' do
access.allowed?
expect(user).to be_blocked
expect(user).to be_ldap_blocked
@@ -78,6 +78,31 @@ describe Gitlab::LDAP::Access, lib: true do
end
it { is_expected.to be_truthy }
+
+ context 'when user cannot be found' do
+ before do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(nil)
+ end
+
+ it { is_expected.to be_falsey }
+
+ it 'blocks user in GitLab' do
+ access.allowed?
+ expect(user).to be_blocked
+ expect(user).to be_ldap_blocked
+ end
+ end
+
+ context 'when user was previously ldap_blocked' do
+ before do
+ user.ldap_block
+ end
+
+ it 'unblocks the user if it exists' do
+ access.allowed?
+ expect(user).not_to be_blocked
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
new file mode 100644
index 00000000000..e01b0b4bd21
--- /dev/null
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Gitlab::Metrics::Subscribers::RailsCache do
+ let(:transaction) { Gitlab::Metrics::Transaction.new }
+ let(:subscriber) { described_class.new }
+
+ let(:event) { double(:event, duration: 15.2) }
+
+ describe '#cache_read' do
+ it 'increments the cache_read duration' do
+ expect(subscriber).to receive(:increment).
+ with(:cache_read_duration, event.duration)
+
+ subscriber.cache_read(event)
+ end
+ end
+
+ describe '#cache_write' do
+ it 'increments the cache_write duration' do
+ expect(subscriber).to receive(:increment).
+ with(:cache_write_duration, event.duration)
+
+ subscriber.cache_write(event)
+ end
+ end
+
+ describe '#cache_delete' do
+ it 'increments the cache_delete duration' do
+ expect(subscriber).to receive(:increment).
+ with(:cache_delete_duration, event.duration)
+
+ subscriber.cache_delete(event)
+ end
+ end
+
+ describe '#cache_exist?' do
+ it 'increments the cache_exists duration' do
+ expect(subscriber).to receive(:increment).
+ with(:cache_exists_duration, event.duration)
+
+ subscriber.cache_exist?(event)
+ end
+ end
+
+ describe '#increment' do
+ context 'without a transaction' do
+ it 'returns' do
+ expect(transaction).not_to receive(:increment)
+
+ subscriber.increment(:foo, 15.2)
+ end
+ end
+
+ context 'with a transaction' do
+ before do
+ allow(subscriber).to receive(:current_transaction).
+ and_return(transaction)
+ end
+
+ it 'increments the total and specific cache duration' do
+ expect(transaction).to receive(:increment).
+ with(:cache_duration, event.duration)
+
+ expect(transaction).to receive(:increment).
+ with(:cache_delete_duration, event.duration)
+
+ subscriber.increment(:cache_delete_duration, event.duration)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index f8c1d956ca1..d6ae54e25e8 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -26,4 +26,10 @@ describe Gitlab::Metrics::System do
end
end
end
+
+ describe '.cpu_time' do
+ it 'returns a Fixnum' do
+ expect(described_class.cpu_time).to be_an_instance_of(Fixnum)
+ end
+ end
end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 0ec8a6dc5cb..3dee13e27f4 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Metrics do
end
end
- describe '#submit_metrics' do
+ describe '.submit_metrics' do
it 'prepares and writes the metrics to InfluxDB' do
connection = double(:connection)
pool = double(:pool)
@@ -26,7 +26,7 @@ describe Gitlab::Metrics do
end
end
- describe '#prepare_metrics' do
+ describe '.prepare_metrics' do
it 'returns a Hash with the keys as Symbols' do
metrics = described_class.
prepare_metrics([{ 'values' => {}, 'tags' => {} }])
@@ -51,7 +51,7 @@ describe Gitlab::Metrics do
end
end
- describe '#escape_value' do
+ describe '.escape_value' do
it 'escapes an equals sign' do
expect(described_class.escape_value('foo=')).to eq('foo\\=')
end
@@ -60,4 +60,42 @@ describe Gitlab::Metrics do
expect(described_class.escape_value(10)).to eq('10')
end
end
+
+ describe '.measure' do
+ context 'without a transaction' do
+ it 'returns the return value of the block' do
+ val = Gitlab::Metrics.measure(:foo) { 10 }
+
+ expect(val).to eq(10)
+ end
+ end
+
+ context 'with a transaction' do
+ let(:transaction) { Gitlab::Metrics::Transaction.new }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:current_transaction).
+ and_return(transaction)
+ end
+
+ it 'adds a metric to the current transaction' do
+ expect(transaction).to receive(:increment).
+ with('foo_real_time', a_kind_of(Numeric))
+
+ expect(transaction).to receive(:increment).
+ with('foo_cpu_time', a_kind_of(Numeric))
+
+ expect(transaction).to receive(:increment).
+ with('foo_call_count', 1)
+
+ Gitlab::Metrics.measure(:foo) { 10 }
+ end
+
+ it 'returns the return value of the block' do
+ val = Gitlab::Metrics.measure(:foo) { 10 }
+
+ expect(val).to eq(10)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb
index de7cd99d49d..c2a51d9249c 100644
--- a/spec/lib/gitlab/saml/user_spec.rb
+++ b/spec/lib/gitlab/saml/user_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Saml::User, lib: true do
let(:gl_user) { saml_user.gl_user }
let(:uid) { 'my-uid' }
let(:provider) { 'saml' }
- let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) }
+ let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash, extra: { raw_info: OneLogin::RubySaml::Attributes.new({ 'groups' => %w(Developers Freelancers Designers) }) }) }
let(:info_hash) do
{
name: 'John',
@@ -23,10 +23,20 @@ describe Gitlab::Saml::User, lib: true do
allow(Gitlab::LDAP::Config).to receive_messages(messages)
end
+ def stub_basic_saml_config
+ allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', args: {} } })
+ end
+
+ def stub_saml_group_config(groups)
+ allow(Gitlab::Saml::Config).to receive_messages({ options: { name: 'saml', groups_attribute: 'groups', external_groups: groups, args: {} } })
+ end
+
+ before { stub_basic_saml_config }
+
describe 'account exists on server' do
before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) }
+ let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
context 'and should bind with SAML' do
- let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
it 'adds the SAML identity to the existing user' do
saml_user.save
expect(gl_user).to be_valid
@@ -36,6 +46,35 @@ describe Gitlab::Saml::User, lib: true do
expect(identity.provider).to eql 'saml'
end
end
+
+ context 'external groups' do
+ context 'are defined' do
+ it 'marks the user as external' do
+ stub_saml_group_config(%w(Freelancers))
+ saml_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user.external).to be_truthy
+ end
+ end
+
+ before { stub_saml_group_config(%w(Interns)) }
+ context 'are defined but the user does not belong there' do
+ it 'does not mark the user as external' do
+ saml_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user.external).to be_falsey
+ end
+ end
+
+ context 'user was external, now should not be' do
+ it 'should make user internal' do
+ existing_user.update_attribute('external', true)
+ saml_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user.external).to be_falsey
+ end
+ end
+ end
end
describe 'no account exists on server' do
@@ -68,6 +107,26 @@ describe Gitlab::Saml::User, lib: true do
end
end
+ context 'external groups' do
+ context 'are defined' do
+ it 'marks the user as external' do
+ stub_saml_group_config(%w(Freelancers))
+ saml_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user.external).to be_truthy
+ end
+ end
+
+ context 'are defined but the user does not belong there' do
+ it 'does not mark the user as external' do
+ stub_saml_group_config(%w(Interns))
+ saml_user.save
+ expect(gl_user).to be_valid
+ expect(gl_user.external).to be_falsey
+ end
+ end
+ end
+
context 'with auto_link_ldap_user disabled (default)' do
before { stub_omniauth_config({ auto_link_ldap_user: false, auto_link_saml_user: false, allow_single_sign_on: ['saml'] }) }
include_examples 'to verify compliance with allow_single_sign_on'
@@ -76,12 +135,6 @@ describe Gitlab::Saml::User, lib: true do
context 'with auto_link_ldap_user enabled' do
before { stub_omniauth_config({ auto_link_ldap_user: true, auto_link_saml_user: false }) }
- context 'and no LDAP provider defined' do
- before { stub_ldap_config(providers: []) }
-
- include_examples 'to verify compliance with allow_single_sign_on'
- end
-
context 'and at least one LDAP provider is defined' do
before { stub_ldap_config(providers: %w(ldapmain)) }
@@ -89,19 +142,18 @@ describe Gitlab::Saml::User, lib: true do
before do
allow(ldap_user).to receive(:uid) { uid }
allow(ldap_user).to receive(:username) { uid }
- allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
+ allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) }
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
end
context 'and no account for the LDAP user' do
-
it 'creates a user with dual LDAP and SAML identities' do
saml_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
- expect(gl_user.email).to eql 'johndoe@example.com'
+ expect(gl_user.email).to eql 'john@mail.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
@@ -111,13 +163,13 @@ describe Gitlab::Saml::User, lib: true do
end
context 'and LDAP user has an account already' do
- let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
- it "adds the omniauth identity to the LDAP account" do
+ let!(:existing_user) { create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
+ it 'adds the omniauth identity to the LDAP account' do
saml_user.save
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
- expect(gl_user.email).to eql 'john@example.com'
+ expect(gl_user.email).to eql 'john@mail.com'
expect(gl_user.identities.length).to eql 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
@@ -126,19 +178,13 @@ describe Gitlab::Saml::User, lib: true do
end
end
end
-
- context 'and no corresponding LDAP person' do
- before { allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) }
-
- include_examples 'to verify compliance with allow_single_sign_on'
- end
end
end
end
describe 'blocking' do
- before { stub_omniauth_config({ allow_saml_sign_up: true, auto_link_saml_user: true }) }
+ before { stub_omniauth_config({ allow_single_sign_on: ['saml'], auto_link_saml_user: true }) }
context 'signup with SAML only' do
context 'dont block on create' do
@@ -162,64 +208,6 @@ describe Gitlab::Saml::User, lib: true do
end
end
- context 'signup with linked omniauth and LDAP account' do
- before do
- stub_omniauth_config(auto_link_ldap_user: true)
- allow(ldap_user).to receive(:uid) { uid }
- allow(ldap_user).to receive(:username) { uid }
- allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
- allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
- allow(saml_user).to receive(:ldap_person).and_return(ldap_user)
- end
-
- context "and no account for the LDAP user" do
- context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).not_to be_blocked
- end
- end
-
- context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).to be_blocked
- end
- end
- end
-
- context 'and LDAP user has an account already' do
- let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
-
- context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).not_to be_blocked
- end
- end
-
- context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).not_to be_blocked
- end
- end
- end
- end
-
-
context 'sign-in' do
before do
saml_user.save
@@ -245,26 +233,6 @@ describe Gitlab::Saml::User, lib: true do
expect(gl_user).not_to be_blocked
end
end
-
- context 'dont block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).not_to be_blocked
- end
- end
-
- context 'block on create (LDAP)' do
- before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
-
- it do
- saml_user.save
- expect(gl_user).to be_valid
- expect(gl_user).not_to be_blocked
- end
- end
end
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 9b47acfe0cd..631b5094f42 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -35,7 +35,9 @@ describe Notify do
subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
it_behaves_like 'an assignee email'
- it_behaves_like 'an email starting a new thread', 'issue'
+ it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -73,9 +75,11 @@ describe Notify do
subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -104,7 +108,9 @@ describe Notify do
subject { Notify.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
@@ -132,7 +138,9 @@ describe Notify do
let(:status) { 'closed' }
subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -163,7 +171,9 @@ describe Notify do
let(:new_issue) { create(:issue) }
subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -196,9 +206,11 @@ describe Notify do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
it_behaves_like 'an assignee email'
- it_behaves_like 'an email starting a new thread', 'merge_request'
+ it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'has the correct subject' do
is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -216,10 +228,6 @@ describe Notify do
is_expected.to have_body_text /#{merge_request.target_branch}/
end
- it 'has the correct message-id set' do
- is_expected.to have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>"
- end
-
context 'when enabled email_author_in_body' do
before do
allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
@@ -247,7 +255,9 @@ describe Notify do
subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like "an unsubscribeable thread"
@@ -278,7 +288,9 @@ describe Notify do
subject { Notify.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
@@ -306,9 +318,11 @@ describe Notify do
let(:status) { 'reopened' }
subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) }
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -337,9 +351,11 @@ describe Notify do
subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
@@ -456,9 +472,11 @@ describe Notify do
subject { Notify.note_commit_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'commit'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { commit }
+ end
it_behaves_like 'it should show Gmail Actions View Commit link'
- it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'a user cannot unsubscribe through footer link'
it 'has the correct subject' do
is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
@@ -477,7 +495,9 @@ describe Notify do
subject { Notify.note_merge_request_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
@@ -498,7 +518,9 @@ describe Notify do
subject { Notify.note_issue_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb
index 6019af544d3..5a85cb501dd 100644
--- a/spec/mailers/shared/notify.rb
+++ b/spec/mailers/shared/notify.rb
@@ -10,6 +10,13 @@ shared_context 'gitlab email notification' do
ActionMailer::Base.deliveries.clear
email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email)
+ stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}")
+ end
+end
+
+shared_context 'reply-by-email is enabled with incoming address without %{key}' do
+ before do
+ stub_incoming_email_setting(enabled: true, address: "reply@#{Gitlab.config.gitlab.host}")
end
end
@@ -46,25 +53,76 @@ shared_examples 'an email with X-GitLab headers containing project details' do
end
end
-shared_examples 'an email starting a new thread' do |message_id_prefix|
- include_examples 'an email with X-GitLab headers containing project details'
+shared_examples 'a new thread email with reply-by-email enabled' do
+ let(:regex) { /\A<reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
+
+ it 'has a Message-ID header' do
+ is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
+ end
- it 'has a discussion identifier' do
- is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ it 'has a References header' do
+ is_expected.to have_header 'References', regex
end
end
-shared_examples 'an answer to an existing thread' do |thread_id_prefix|
+shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
+ let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> <reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
+
+ it 'has a Message-ID header' do
+ is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+
+ it 'has a In-Reply-To header' do
+ is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
+ end
+
+ it 'has a References header' do
+ is_expected.to have_header 'References', regex
+ end
it 'has a subject that begins with Re: ' do
is_expected.to have_subject /^Re: /
end
+end
+
+shared_examples 'an email starting a new thread with reply-by-email enabled' do
+ include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'a new thread email with reply-by-email enabled'
+
+ context 'when reply-by-email is enabled with incoming address with %{key}' do
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+
+ context 'when reply-by-email is enabled with incoming address without %{key}' do
+ include_context 'reply-by-email is enabled with incoming address without %{key}'
+ include_examples 'a new thread email with reply-by-email enabled'
+
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+end
+
+shared_examples 'an answer to an existing thread with reply-by-email enabled' do
+ include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'a thread answer email with reply-by-email enabled'
+
+ context 'when reply-by-email is enabled with incoming address with %{key}' do
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+
+ context 'when reply-by-email is enabled with incoming address without %{key}' do
+ include_context 'reply-by-email is enabled with incoming address without %{key}'
+ include_examples 'a thread answer email with reply-by-email enabled'
- it 'has headers that reference an existing thread' do
- is_expected.to have_header 'Message-ID', /<(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/
+ end
end
end
@@ -83,10 +141,12 @@ shared_examples 'a new user email' do
end
shared_examples 'it should have Gmail Actions links' do
+ it { is_expected.to have_body_text '<script type="application/ld+json">' }
it { is_expected.to have_body_text /ViewAction/ }
end
shared_examples 'it should not have Gmail Actions links' do
+ it { is_expected.to_not have_body_text '<script type="application/ld+json">' }
it { is_expected.to_not have_body_text /ViewAction/ }
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index b1764d7ac09..520cf1b75de 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -12,7 +12,6 @@
# updated_at :datetime
# home_page_url :string(255)
# default_branch_protection :integer default(2)
-# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index be29b6d66ff..b16ccc6e305 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -9,6 +9,7 @@ describe Issue, "Issuable" do
it { is_expected.to belong_to(:author) }
it { is_expected.to belong_to(:assignee) }
it { is_expected.to have_many(:notes).dependent(:destroy) }
+ it { is_expected.to have_many(:todos).dependent(:destroy) }
end
describe "Validation" do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 5fe44246738..89909c2bcd7 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -59,44 +59,70 @@ describe Event, models: true do
end
it { expect(@event.push?).to be_truthy }
- it { expect(@event.proper?).to be_truthy }
+ it { expect(@event.visible_to_user?).to be_truthy }
it { expect(@event.tag?).to be_falsey }
it { expect(@event.branch_name).to eq("master") }
it { expect(@event.author).to eq(@user) }
end
- describe '#proper?' do
- context 'issue event' do
- let(:project) { create(:empty_project, :public) }
- let(:non_member) { create(:user) }
- let(:member) { create(:user) }
- let(:author) { create(:author) }
- let(:assignee) { create(:user) }
- let(:admin) { create(:admin) }
- let(:event) { Event.new(project: project, action: Event::CREATED, target: issue, author_id: author.id) }
-
- before do
- project.team << [member, :developer]
- end
+ describe '#visible_to_user?' do
+ let(:project) { create(:empty_project, :public) }
+ let(:non_member) { create(:user) }
+ let(:member) { create(:user) }
+ let(:author) { create(:author) }
+ let(:assignee) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:issue) { create(:issue, project: project, author: author, assignee: assignee) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
+ let(:note_on_issue) { create(:note_on_issue, noteable: issue, project: project) }
+ let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project) }
+ let(:event) { Event.new(project: project, target: target, author_id: author.id) }
+ before do
+ project.team << [member, :developer]
+ end
+
+ context 'issue event' do
context 'for non confidential issues' do
- let(:issue) { create(:issue, project: project, author: author, assignee: assignee) }
+ let(:target) { issue }
- it { expect(event.proper?(non_member)).to eq true }
- it { expect(event.proper?(author)).to eq true }
- it { expect(event.proper?(assignee)).to eq true }
- it { expect(event.proper?(member)).to eq true }
- it { expect(event.proper?(admin)).to eq true }
+ it { expect(event.visible_to_user?(non_member)).to eq true }
+ it { expect(event.visible_to_user?(author)).to eq true }
+ it { expect(event.visible_to_user?(assignee)).to eq true }
+ it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(admin)).to eq true }
end
context 'for confidential issues' do
- let(:issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
+ let(:target) { confidential_issue }
+
+ it { expect(event.visible_to_user?(non_member)).to eq false }
+ it { expect(event.visible_to_user?(author)).to eq true }
+ it { expect(event.visible_to_user?(assignee)).to eq true }
+ it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(admin)).to eq true }
+ end
+ end
+
+ context 'note event' do
+ context 'on non confidential issues' do
+ let(:target) { note_on_issue }
+
+ it { expect(event.visible_to_user?(non_member)).to eq true }
+ it { expect(event.visible_to_user?(author)).to eq true }
+ it { expect(event.visible_to_user?(assignee)).to eq true }
+ it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(admin)).to eq true }
+ end
+
+ context 'on confidential issues' do
+ let(:target) { note_on_confidential_issue }
- it { expect(event.proper?(non_member)).to eq false }
- it { expect(event.proper?(author)).to eq true }
- it { expect(event.proper?(assignee)).to eq true }
- it { expect(event.proper?(member)).to eq true }
- it { expect(event.proper?(admin)).to eq true }
+ it { expect(event.visible_to_user?(non_member)).to eq false }
+ it { expect(event.visible_to_user?(author)).to eq true }
+ it { expect(event.visible_to_user?(assignee)).to eq true }
+ it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(admin)).to eq true }
end
end
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index fd1513cab1b..56a9fbe9720 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -20,24 +20,27 @@ require "spec_helper"
describe SystemHook, models: true do
describe "execute" do
- before(:each) do
- @system_hook = create(:system_hook)
- WebMock.stub_request(:post, @system_hook.url)
+ let(:system_hook) { create(:system_hook) }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:group) { create(:group) }
+
+ before do
+ WebMock.stub_request(:post, system_hook.url)
end
it "project_create hook" do
- Projects::CreateService.new(create(:user), name: 'empty').execute
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ Projects::CreateService.new(user, name: 'empty').execute
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_destroy hook" do
- user = create(:user)
- project = create(:empty_project, namespace: user.namespace)
Projects::DestroyService.new(project, user, {}).pending_delete!
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
@@ -45,37 +48,36 @@ describe SystemHook, models: true do
it "user_create hook" do
create(:user)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "user_destroy hook" do
- user = create(:user)
user.destroy
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_create hook" do
- user = create(:user)
- project = create(:project)
project.team << [user, :master]
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_destroy hook" do
- user = create(:user)
- project = create(:project)
project.team << [user, :master]
project.project_members.destroy_all
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_team/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
@@ -83,41 +85,39 @@ describe SystemHook, models: true do
it 'group create hook' do
create(:group)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group destroy hook' do
- group = create(:group)
group.destroy
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group member create hook' do
- group = create(:group)
- user = create(:user)
group.add_master(user)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group member destroy hook' do
- group = create(:group)
- user = create(:user)
group.add_master(user)
group.group_members.destroy_all
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_group/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
-
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index bd0a4ebe337..6f5d912fe5d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -224,22 +224,22 @@ describe MergeRequest, models: true do
['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
it "detects the '#{wip_prefix}' prefix" do
subject.title = "#{wip_prefix}#{subject.title}"
- expect(subject).to be_work_in_progress
+ expect(subject.work_in_progress?).to eq true
end
end
it "doesn't detect WIP for words starting with WIP" do
subject.title = "Wipwap #{subject.title}"
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
it "doesn't detect WIP for words containing with WIP" do
subject.title = "WupWipwap #{subject.title}"
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
it "doesn't detect WIP by default" do
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
end
diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb
index 905379a64e3..2ccbff553f0 100644
--- a/spec/models/project_services/builds_email_service_spec.rb
+++ b/spec/models/project_services/builds_email_service_spec.rb
@@ -6,18 +6,38 @@ describe BuildsEmailService do
let(:service) { BuildsEmailService.new }
describe :execute do
- it "sends email" do
+ it 'sends email' do
service.recipients = 'test@gitlab.com'
data[:build_status] = 'failed'
expect(BuildEmailWorker).to receive(:perform_async)
service.execute(data)
end
- it "does not sends email with failed build and allowed_failure on" do
+ it 'does not send email with succeeded build and notify_only_broken_builds on' do
+ expect(service).to receive(:notify_only_broken_builds).and_return(true)
+ data[:build_status] = 'success'
+ expect(BuildEmailWorker).not_to receive(:perform_async)
+ service.execute(data)
+ end
+
+ it 'does not send email with failed build and build_allow_failure is true' do
data[:build_status] = 'failed'
data[:build_allow_failure] = true
expect(BuildEmailWorker).not_to receive(:perform_async)
service.execute(data)
end
+
+ it 'does not send email with unknown build status' do
+ data[:build_status] = 'foo'
+ expect(BuildEmailWorker).not_to receive(:perform_async)
+ service.execute(data)
+ end
+
+ it 'does not send email when recipients list is empty' do
+ service.recipients = ' ,, '
+ data[:build_status] = 'failed'
+ expect(BuildEmailWorker).not_to receive(:perform_async)
+ service.execute(data)
+ end
end
end
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
index 97e6f03e308..f648cbe2dee 100644
--- a/spec/models/project_services/slack_service/issue_message_spec.rb
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -27,6 +27,16 @@ describe SlackService::IssueMessage, models: true do
let(:color) { '#345' }
+ context '#initialize' do
+ before do
+ args[:object_attributes][:description] = nil
+ end
+
+ it 'returns a non-null description' do
+ expect(subject.description).to eq('')
+ end
+ end
+
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 20f06f4b7e1..f29c389e094 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -104,6 +104,15 @@ describe Project, models: true do
end
end
+ describe 'default_scope' do
+ it 'excludes projects pending deletion from the results' do
+ project = create(:empty_project)
+ create(:empty_project, pending_delete: true)
+
+ expect(Project.all).to eq [project]
+ end
+ end
+
describe 'project token' do
it 'should set an random token if none provided' do
project = FactoryGirl.create :empty_project, runners_token: ''
@@ -422,6 +431,12 @@ describe Project, models: true do
it { should eq "http://localhost#{avatar_path}" }
end
+
+ context 'when git repo is empty' do
+ let(:project) { create(:empty_project) }
+
+ it { should eq nil }
+ end
end
describe :ci_commit do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 7eac70ae948..4e49c413f23 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe Repository, models: true do
include RepoHelpers
+ TestBlob = Struct.new(:name)
let(:repository) { create(:project).repository }
let(:user) { create(:user) }
@@ -131,7 +132,6 @@ describe Repository, models: true do
describe "#license" do
before do
repository.send(:cache).expire(:license)
- TestBlob = Struct.new(:name)
end
it 'test selection preference' do
@@ -148,6 +148,25 @@ describe Repository, models: true do
end
end
+ describe "#gitlab_ci_yml" do
+ it 'returns valid file' do
+ files = [TestBlob.new('file'), TestBlob.new('.gitlab-ci.yml'), TestBlob.new('copying')]
+ expect(repository.tree).to receive(:blobs).and_return(files)
+
+ expect(repository.gitlab_ci_yml.name).to eq('.gitlab-ci.yml')
+ end
+
+ it 'returns nil if not exists' do
+ expect(repository.tree).to receive(:blobs).and_return([])
+ expect(repository.gitlab_ci_yml).to be_nil
+ end
+
+ it 'returns nil for empty repository' do
+ expect(repository).to receive(:empty?).and_return(true)
+ expect(repository.gitlab_ci_yml).to be_nil
+ end
+ end
+
describe :add_branch do
context 'when pre hooks were successful' do
it 'should run without errors' do
@@ -284,7 +303,7 @@ describe Repository, models: true do
describe 'when there are no branches' do
before do
- allow(repository.raw_repository).to receive(:branch_count).and_return(0)
+ allow(repository).to receive(:branch_count).and_return(0)
end
it { is_expected.to eq(false) }
@@ -292,13 +311,13 @@ describe Repository, models: true do
describe 'when there are branches' do
it 'returns true' do
- expect(repository.raw_repository).to receive(:branch_count).and_return(3)
+ expect(repository).to receive(:branch_count).and_return(3)
expect(subject).to eq(true)
end
it 'caches the output' do
- expect(repository.raw_repository).to receive(:branch_count).
+ expect(repository).to receive(:branch_count).
once.
and_return(3)
@@ -417,7 +436,7 @@ describe Repository, models: true do
it 'expires the visible content cache' do
repository.has_visible_content?
- expect(repository.raw_repository).to receive(:branch_count).
+ expect(repository).to receive(:branch_count).
once.
and_return(0)
@@ -539,7 +558,7 @@ describe Repository, models: true do
end
it 'flushes the exists cache' do
- expect(repository).to receive(:expire_exists_cache)
+ expect(repository).to receive(:expire_exists_cache).twice
repository.before_delete
end
@@ -593,6 +612,20 @@ describe Repository, models: true do
end
end
+ describe '#before_import' do
+ it 'flushes the emptiness cachess' do
+ expect(repository).to receive(:expire_emptiness_caches)
+
+ repository.before_import
+ end
+
+ it 'flushes the exists cache' do
+ expect(repository).to receive(:expire_exists_cache)
+
+ repository.before_import
+ end
+ end
+
describe '#after_import' do
it 'flushes the emptiness cachess' do
expect(repository).to receive(:expire_emptiness_caches)
@@ -637,6 +670,19 @@ describe Repository, models: true do
repository.after_create
end
+
+ it 'flushes the root ref cache' do
+ expect(repository).to receive(:expire_root_ref_cache)
+
+ repository.after_create
+ end
+
+ it 'flushes the emptiness caches' do
+ expect(repository).to receive(:expire_emptiness_caches)
+
+ repository.after_create
+ end
+
end
describe "#main_language" do
@@ -725,6 +771,12 @@ describe Repository, models: true do
end
describe '#avatar' do
+ it 'returns nil if repo does not exist' do
+ expect(repository).to receive(:exists?).and_return(false)
+
+ expect(repository.avatar).to eq(nil)
+ end
+
it 'returns the first avatar file found in the repository' do
expect(repository).to receive(:blob_at_branch).
with('master', 'logo.png').
@@ -840,4 +892,21 @@ describe Repository, models: true do
repository.build_cache
end
end
+
+ describe '#local_branches' do
+ it 'returns the local branches' do
+ masterrev = repository.find_branch('master').target
+ create_remote_branch('joe', 'remote_branch', masterrev)
+ repository.add_branch(user, 'local_branch', masterrev)
+
+ expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
+ expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
+ end
+ end
+
+ def create_remote_branch(remote_name, branch_name, target)
+ rugged = repository.rugged
+ rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target)
+ end
+
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 0ab7fd88ce6..8b2fb77e28e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -173,6 +173,13 @@ describe User, models: true do
expect(user).to be_invalid
end
end
+
+ context 'owns_notification_email' do
+ it 'accepts temp_oauth_email emails' do
+ user = build(:user, email: "temp-email-for-oauth@example.com")
+ expect(user).to be_valid
+ end
+ end
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index ce55cb7b0ae..822d3ad3017 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -318,6 +318,17 @@ describe API::API, api: true do
'is too long (maximum is 255 characters)'
])
end
+
+ context 'when an admin or owner makes the request' do
+ it "accepts the creation date to be set" do
+ post api("/projects/#{project.id}/issues", user),
+ title: 'new issue', labels: 'label, label2', created_at: 2.weeks.ago
+
+ expect(response.status).to eq(201)
+ # this take about a second, so probably not equal
+ expect(Time.parse(json_response['created_at'])).to be <= 2.weeks.ago
+ end
+ end
end
describe 'POST /projects/:id/issues with spam filtering' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 667f0dbea5c..6943ff9d26c 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -23,13 +23,25 @@ describe API::API, api: true do
end
describe 'POST /projects/:id/labels' do
- it 'should return created label' do
+ it 'should return created label when all params' do
+ post api("/projects/#{project.id}/labels", user),
+ name: 'Foo',
+ color: '#FFAABB',
+ description: 'test'
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq('Foo')
+ expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['description']).to eq('test')
+ end
+
+ it 'should return created label when only required params' do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAABB'
expect(response.status).to eq(201)
expect(json_response['name']).to eq('Foo')
expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['description']).to be_nil
end
it 'should return a 400 bad request if name not given' do
@@ -94,14 +106,16 @@ describe API::API, api: true do
end
describe 'PUT /projects/:id/labels' do
- it 'should return 200 if name and colors are changed' do
+ it 'should return 200 if name and colors and description are changed' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
new_name: 'New Label',
- color: '#FFFFFF'
+ color: '#FFFFFF',
+ description: 'test'
expect(response.status).to eq(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['description']).to eq('test')
end
it 'should return 200 if name is changed' do
@@ -122,6 +136,15 @@ describe API::API, api: true do
expect(json_response['color']).to eq('#FFFFFF')
end
+ it 'should return 200 if description is changed' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ description: 'test'
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(label1.name)
+ expect(json_response['description']).to eq('test')
+ end
+
it 'should return 404 if label does not exist' do
put api("/projects/#{project.id}/labels", user),
name: 'label2',
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index c9175a4d6eb..25fa30b2f21 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -118,6 +118,7 @@ describe API::API, api: true do
expect(response.status).to eq(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
+ expect(json_response['work_in_progress']).to eq(false)
expect(json_response['merge_status']).to eq('can_be_merged')
end
@@ -133,6 +134,16 @@ describe API::API, api: true do
get api("/projects/#{project.id}/merge_requests/999", user)
expect(response.status).to eq(404)
end
+
+ context 'Work in Progress' do
+ let!(:merge_request_wip) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "WIP: Test", created_at: base_time + 1.second) }
+
+ it "should return merge_request" do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user)
+ expect(response.status).to eq(200)
+ expect(json_response['work_in_progress']).to eq(true)
+ end
+ end
end
describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index db0f6e3c0f5..344f0fe0b7f 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -4,6 +4,7 @@ describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
+ let!(:closed_milestone) { create(:closed_milestone, project: project) }
let!(:milestone) { create(:milestone, project: project) }
before { project.team << [user, :developer] }
@@ -20,6 +21,24 @@ describe API::API, api: true do
get api("/projects/#{project.id}/milestones")
expect(response.status).to eq(401)
end
+
+ it 'returns an array of active milestones' do
+ get api("/projects/#{project.id}/milestones?state=active", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(milestone.id)
+ end
+
+ it 'returns an array of closed milestones' do
+ get api("/projects/#{project.id}/milestones?state=closed", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_milestone.id)
+ end
end
describe 'GET /projects/:id/milestones/:milestone_id' do
@@ -31,10 +50,12 @@ describe API::API, api: true do
end
it 'should return a project milestone by iid' do
- get api("/projects/#{project.id}/milestones?iid=#{milestone.iid}", user)
+ get api("/projects/#{project.id}/milestones?iid=#{closed_milestone.iid}", user)
+
expect(response.status).to eq 200
- expect(json_response.first['title']).to eq milestone.title
- expect(json_response.first['id']).to eq milestone.id
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['title']).to eq closed_milestone.title
+ expect(json_response.first['id']).to eq closed_milestone.id
end
it 'should return 401 error if user not authenticated' do
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 39f9a06fe1b..a467bc935af 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -241,4 +241,65 @@ describe API::API, api: true do
end
end
+ describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do
+ context 'when noteable is an Issue' do
+ it 'deletes a note' do
+ delete api("/projects/#{project.id}/issues/#{issue.id}/"\
+ "notes/#{issue_note.id}", user)
+
+ expect(response.status).to eq(200)
+ # Check if note is really deleted
+ delete api("/projects/#{project.id}/issues/#{issue.id}/"\
+ "notes/#{issue_note.id}", user)
+ expect(response.status).to eq(404)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ delete api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when noteable is a Snippet' do
+ it 'deletes a note' do
+ delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
+ "notes/#{snippet_note.id}", user)
+
+ expect(response.status).to eq(200)
+ # Check if note is really deleted
+ delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
+ "notes/#{snippet_note.id}", user)
+ expect(response.status).to eq(404)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\
+ "notes/123", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when noteable is a Merge Request' do
+ it 'deletes a note' do
+ delete api("/projects/#{project.id}/merge_requests/"\
+ "#{merge_request.id}/notes/#{merge_request_note.id}", user)
+
+ expect(response.status).to eq(200)
+ # Check if note is really deleted
+ delete api("/projects/#{project.id}/merge_requests/"\
+ "#{merge_request.id}/notes/#{merge_request_note.id}", user)
+ expect(response.status).to eq(404)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ delete api("/projects/#{project.id}/merge_requests/"\
+ "#{merge_request.id}/notes/123", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
end
diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb
index 4301588b16a..c112ca5e3ca 100644
--- a/spec/requests/api/project_members_spec.rb
+++ b/spec/requests/api/project_members_spec.rb
@@ -118,8 +118,10 @@ describe API::API, api: true do
end
describe "DELETE /projects/:id/members/:user_id" do
- before { project_member }
- before { project_member2 }
+ before do
+ project_member
+ project_member2
+ end
it "should remove user from project team" do
expect do
@@ -132,6 +134,7 @@ describe API::API, api: true do
expect do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
end.to_not change { ProjectMember.count }
+ expect(response.status).to eq(200)
end
it "should return 200 if team member already removed" do
@@ -145,8 +148,19 @@ describe API::API, api: true do
delete api("/projects/#{project.id}/members/1000000", user)
end.to change { ProjectMember.count }.by(0)
expect(response.status).to eq(200)
- expect(json_response['message']).to eq("Access revoked")
expect(json_response['id']).to eq(1000000)
+ expect(json_response['message']).to eq('Access revoked')
+ end
+
+ context 'when the user is not an admin or owner' do
+ it 'can leave the project' do
+ expect do
+ delete api("/projects/#{project.id}/members/#{user3.id}", user3)
+ end.to change { ProjectMember.count }.by(-1)
+
+ expect(response.status).to eq(200)
+ expect(json_response['id']).to eq(project_member2.id)
+ end
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index a5d4985dc78..be2034e0f39 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -948,6 +948,78 @@ describe API::API, api: true do
end
end
+ describe 'POST /projects/:id/archive' do
+ context 'on an unarchived project' do
+ it 'archives the project' do
+ post api("/projects/#{project.id}/archive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'remains archived' do
+ post api("/projects/#{project.id}/archive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post api("/projects/#{project.id}/archive", user3)
+
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/unarchive' do
+ context 'on an unarchived project' do
+ it 'remains unarchived' do
+ post api("/projects/#{project.id}/unarchive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'unarchives the project' do
+ post api("/projects/#{project.id}/unarchive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post api("/projects/#{project.id}/unarchive", user3)
+
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
describe 'DELETE /projects/:id' do
context 'when authenticated as user' do
it 'should remove project' do
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index a15be07ed57..9f9c3b1cf4c 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -40,6 +40,23 @@ describe API::API, api: true do
end
end
+ describe 'GET /projects/:id/repository/tags/:tag_name' do
+ let(:tag_name) { project.repository.tag_names.sort.reverse.first }
+
+ it 'returns a specific tag' do
+ get api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(tag_name)
+ end
+
+ it 'returns 404 for an invalid tag name' do
+ get api("/projects/#{project.id}/repository/tags/foobar", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
describe 'POST /projects/:id/repository/tags' do
context 'lightweight tags' do
it 'should create a new tag' do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 8490a729e51..b40a5c1c818 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -159,18 +159,44 @@ describe GitPushService, services: true do
end
describe "Updates main language" do
-
context "before push" do
it { expect(project.main_language).to eq(nil) }
end
context "after push" do
- before do
- @service = execute_service(project, user, @oldrev, @newrev, @ref)
+ def execute
+ execute_service(project, user, @oldrev, @newrev, ref)
end
- it { expect(@service.update_main_language).to eq(true) }
- it { expect(project.main_language).to eq("Ruby") }
+ context "to master" do
+ let(:ref) { @ref }
+
+ context 'when main_language is nil' do
+ it 'obtains the language from the repository' do
+ expect(project.repository).to receive(:main_language)
+ execute
+ end
+
+ it 'sets the project main language' do
+ execute
+ expect(project.main_language).to eq("Ruby")
+ end
+ end
+
+ context 'when main_language is already set' do
+ it 'does not check the repository' do
+ execute # do an initial run to simulate lang being preset
+ expect(project.repository).not_to receive(:main_language)
+ execute
+ end
+ end
+ end
+
+ context "to other branch" do
+ let(:ref) { 'refs/heads/feature/branch' }
+
+ it { expect(project.main_language).to eq(nil) }
+ end
end
end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index ade3b7850f1..2a5e4ac3ec4 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -85,6 +85,10 @@ describe Issues::MoveService, services: true do
expect(old_issue.moved?).to eq true
expect(old_issue.moved_to).to eq new_issue
end
+
+ it 'preserves create time' do
+ expect(old_issue.created_at).to eq new_issue.created_at
+ end
end
context 'issue with notes' do
@@ -121,10 +125,23 @@ describe Issues::MoveService, services: true do
it 'preserves orignal author of comment' do
expect(user_notes.pluck(:author_id)).to all(eq(author.id))
end
+ end
+
+ context 'note that has been updated' do
+ let!(:note) do
+ create(:note, noteable: old_issue, project: old_project,
+ author: author, updated_at: Date.yesterday,
+ created_at: Date.yesterday)
+ end
+
+ include_context 'issue move executed'
it 'preserves time when note has been created at' do
- expect(old_issue.notes.first.created_at)
- .to eq new_issue.notes.first.created_at
+ expect(new_issue.notes.first.created_at).to eq note.created_at
+ end
+
+ it 'preserves time when note has been updated at' do
+ expect(new_issue.notes.first.updated_at).to eq note.updated_at
end
end
@@ -143,6 +160,20 @@ describe Issues::MoveService, services: true do
.to eq "Note with reference to merge request #{old_project.to_reference}!1"
end
end
+
+ context 'issue description with uploads' do
+ let(:uploader) { build(:file_uploader, project: old_project) }
+ let(:description) { "Text and #{uploader.to_markdown}" }
+
+ include_context 'issue move executed'
+
+ it 'rewrites uploads in description' do
+ expect(new_issue.description).to_not eq description
+ expect(new_issue.description)
+ .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
+ expect(new_issue.description).to_not include uploader.secret
+ end
+ end
end
describe 'rewritting references' do
diff --git a/spec/services/notes/delete_service_spec.rb b/spec/services/notes/delete_service_spec.rb
new file mode 100644
index 00000000000..1d0a747a480
--- /dev/null
+++ b/spec/services/notes/delete_service_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe Notes::DeleteService, services: true do
+ describe '#execute' do
+ it 'deletes a note' do
+ project = create(:empty_project)
+ issue = create(:issue, project: project)
+ note = create(:note, project: project, noteable: issue)
+
+ described_class.new(project, note.author).execute(note)
+
+ expect(project.issues.find(issue.id).notes).not_to include(note)
+ end
+ end
+end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 04f474c736c..32bf3acf483 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -72,6 +72,23 @@ describe Projects::ImportService, services: true do
expect(result[:status]).to eq :success
end
+ it 'flushes various caches' do
+ expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).
+ with(project.path_with_namespace, project.import_url).
+ and_return(true)
+
+ expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).
+ and_return(true)
+
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
+ and_call_original
+
+ expect_any_instance_of(Repository).to receive(:expire_exists_cache).
+ and_call_original
+
+ subject.execute
+ end
+
it 'fails if importer fails' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false)
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
new file mode 100644
index 00000000000..23f5555d3e0
--- /dev/null
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Projects::UnlinkForkService, services: true do
+ subject { Projects::UnlinkForkService.new(fork_project, user) }
+
+ let(:fork_link) { create(:forked_project_link) }
+ let(:fork_project) { fork_link.forked_to_project }
+ let(:user) { create(:user) }
+
+ context 'with opened merge request on the source project' do
+ let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: fork_link.forked_from_project) }
+ let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) }
+
+ before do
+ allow(MergeRequests::CloseService).to receive(:new).
+ with(fork_project, user).
+ and_return(mr_close_service)
+ end
+
+ it 'close all pending merge requests' do
+ expect(mr_close_service).to receive(:execute).with(merge_request)
+
+ subject.execute
+ end
+ end
+
+ it 'remove fork relation' do
+ expect(fork_project.forked_project_link).to receive(:destroy)
+
+ subject.execute
+ end
+end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index b4728807b8b..82b7fbfa816 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -2,22 +2,25 @@ require 'spec_helper'
describe TodoService, services: true do
let(:author) { create(:user) }
- let(:john_doe) { create(:user, username: 'john_doe') }
- let(:michael) { create(:user, username: 'michael') }
- let(:stranger) { create(:user, username: 'stranger') }
+ let(:assignee) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:member) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:john_doe) { create(:user) }
let(:project) { create(:project) }
- let(:mentions) { [author.to_reference, john_doe.to_reference, michael.to_reference, stranger.to_reference].join(' ') }
+ let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') }
let(:service) { described_class.new }
before do
project.team << [author, :developer]
+ project.team << [member, :developer]
project.team << [john_doe, :developer]
- project.team << [michael, :developer]
end
describe 'Issues' do
let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: mentions) }
let(:unassigned_issue) { create(:issue, project: project, assignee: nil) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) }
describe '#new_issue' do
it 'creates a todo if assigned' do
@@ -37,10 +40,20 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_issue(issue, author)
- should_create_todo(user: michael, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
+ end
+
+ it 'does not create todo for non project members when issue is confidential' do
+ service.new_issue(confidential_issue, john_doe)
+
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
end
@@ -48,16 +61,26 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.update_issue(issue, author)
- should_create_todo(user: michael, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
it 'does not create a todo if user was already mentioned' do
- create(:todo, :mentioned, user: michael, project: project, target: issue, author: author)
+ create(:todo, :mentioned, user: member, project: project, target: issue, author: author)
- expect { service.update_issue(issue, author) }.not_to change(michael.todos, :count)
+ expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
+ end
+
+ it 'does not create todo for non project members when issue is confidential' do
+ service.update_issue(confidential_issue, john_doe)
+
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
end
@@ -109,8 +132,10 @@ describe TodoService, services: true do
describe '#new_note' do
let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
let(:note) { create(:note, project: project, noteable: issue, author: john_doe, note: mentions) }
let(:note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: mentions) }
+ let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: mentions) }
let(:note_on_project_snippet) { create(:note_on_project_snippet, project: project, author: john_doe, note: mentions) }
let(:award_note) { create(:note, :award, project: project, noteable: issue, author: john_doe, note: 'thumbsup') }
let(:system_note) { create(:system_note, project: project, noteable: issue) }
@@ -142,19 +167,29 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_note(note, john_doe)
- should_create_todo(user: michael, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
- should_not_create_todo(user: stranger, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ end
+
+ it 'does not create todo for non project members when leaving a note on a confidential issue' do
+ service.new_note(note_on_confidential_issue, john_doe)
+
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end
it 'creates a todo for each valid mentioned user when leaving a note on commit' do
service.new_note(note_on_commit, john_doe)
- should_create_todo(user: michael, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+ should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
- should_not_create_todo(user: stranger, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+ should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
end
it 'does not create todo when leaving a note on snippet' do
@@ -185,10 +220,10 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_merge_request(mr_assigned, author)
- should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
end
@@ -196,16 +231,16 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.update_merge_request(mr_assigned, author)
- should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
it 'does not create a todo if user was already mentioned' do
- create(:todo, :mentioned, user: michael, project: project, target: mr_assigned, author: author)
+ create(:todo, :mentioned, user: member, project: project, target: mr_assigned, author: author)
- expect { service.update_merge_request(mr_assigned, author) }.not_to change(michael.todos, :count)
+ expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count)
end
end
diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb
new file mode 100644
index 00000000000..aa89afd8fb3
--- /dev/null
+++ b/spec/support/carrierwave.rb
@@ -0,0 +1,7 @@
+CarrierWave.root = 'tmp/tests/uploads'
+
+RSpec.configure do |config|
+ config.after(:suite) do
+ FileUtils.rm_rf('tmp/tests/uploads')
+ end
+end
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index ef5ea7d626e..e849a9633b9 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -78,6 +78,6 @@ module FilterSpecHelper
# Shortcut to Rails' auto-generated routes helpers, to avoid including the
# module
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
end
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 73c6792b65f..b87cd6bbca2 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -106,7 +106,7 @@ class MarkdownFeature
end
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
def raw_markdown
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 1d52489e804..43cb6ef43f2 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -13,7 +13,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- link = actual.at_css('a:contains("Relative Link")')
+ link = actual.at_css('a:contains("Relative Link")')
image = actual.at_css('img[alt="Relative Image"]')
expect(link['href']).to end_with('master/doc/README.md')
@@ -72,14 +72,15 @@ module MarkdownMatchers
have_css("img[src$='#{src}']")
end
+ prefix = '/namespace1/gitlabhq/wikis'
set_default_markdown_messages
match do |actual|
- expect(actual).to have_link('linked-resource', href: 'linked-resource')
- expect(actual).to have_link('link-text', href: 'linked-resource')
+ expect(actual).to have_link('linked-resource', href: "#{prefix}/linked-resource")
+ expect(actual).to have_link('link-text', href: "#{prefix}/linked-resource")
expect(actual).to have_link('http://example.com', href: 'http://example.com')
expect(actual).to have_link('link-text', href: 'http://example.com/pdfs/gollum.pdf')
- expect(actual).to have_image('/gitlabhq/wikis/images/example.jpg')
+ expect(actual).to have_image("#{prefix}/images/example.jpg")
expect(actual).to have_image('http://example.com/images/example.jpg')
end
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 0d1bd030f3c..71664bb192e 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -15,6 +15,7 @@ module TestEnv
'lfs' => 'be93687',
'master' => '5937ac0',
"'test'" => 'e56497b',
+ 'orphaned-branch' => '45127a9',
}
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 320be9a0b61..05fc4c4554f 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -7,8 +7,12 @@ describe 'gitlab:app namespace rake task' do
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/gitlab/db'
+
# empty task as env is already loaded
Rake::Task.define_task :environment
+
+ # We need this directory to run `gitlab:backup:create` task
+ FileUtils.mkdir_p('public/uploads')
end
def run_rake_task(task_name)
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index b11c5de94e3..1abd87d7d33 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -22,6 +22,8 @@ describe MergeWorker do
merge_request.reload
expect(merge_request).to be_merged
+
+ source_project.repository.expire_branches_cache
expect(source_project.repository.branch_names).not_to include('markdown')
end
end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
new file mode 100644
index 00000000000..7e59bd2fced
--- /dev/null
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe ProjectCacheWorker do
+ let(:project) { create(:project) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'updates project cache data' do
+
+ expect_any_instance_of(Repository).to receive(:size)
+ expect_any_instance_of(Repository).to receive(:commit_count)
+
+ expect_any_instance_of(Project).to receive(:update_repository_size)
+ expect_any_instance_of(Project).to receive(:update_commit_count)
+
+ subject.perform(project.id)
+ end
+
+ it 'handles missing repository data' do
+ expect_any_instance_of(Repository).to receive(:exists?).and_return(false)
+ expect_any_instance_of(Repository).not_to receive(:size)
+
+ subject.perform(project.id)
+ end
+ end
+end