summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb24
-rw-r--r--spec/controllers/commit_controller_spec.rb51
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb20
-rw-r--r--spec/controllers/profiles/keys_controller_spec.rb12
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb50
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb8
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb16
-rw-r--r--spec/controllers/users_controller_spec.rb25
-rw-r--r--spec/factories/commits.rb12
-rw-r--r--spec/factories/oauth_access_tokens.rb22
-rw-r--r--spec/factories/oauth_applications.rb9
-rw-r--r--spec/factories/project_wikis.rb7
-rw-r--r--spec/factories/users.rb2
-rw-r--r--spec/factories/wiki_pages.rb9
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb43
-rw-r--r--spec/features/builds_spec.rb30
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb27
-rw-r--r--spec/features/issues/filter_by_labels_spec.rb167
-rw-r--r--spec/features/issues/filter_issues_spec.rb9
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb79
-rw-r--r--spec/features/issues/move_spec.rb10
-rw-r--r--spec/features/issues/update_issues_spec.rb2
-rw-r--r--spec/features/issues_spec.rb186
-rw-r--r--spec/features/markdown_spec.rb7
-rw-r--r--spec/features/merge_requests/cherry_pick_spec.rb44
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb6
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb25
-rw-r--r--spec/features/participants_autocomplete_spec.rb98
-rw-r--r--spec/features/profiles/oauth_applications_spec.rb39
-rw-r--r--spec/features/project/shortcuts_spec.rb21
-rw-r--r--spec/features/projects/commit/builds_spec.rb27
-rw-r--r--spec/features/projects/commits/cherry_pick_spec.rb67
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb61
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb39
-rw-r--r--spec/features/projects/members/anonymous_user_sees_members_spec.rb20
-rw-r--r--spec/features/runners_spec.rb16
-rw-r--r--spec/features/search_spec.rb4
-rw-r--r--spec/features/security/project/internal_access_spec.rb10
-rw-r--r--spec/features/security/project/private_access_spec.rb6
-rw-r--r--spec/features/security/project/public_access_spec.rb12
-rw-r--r--spec/features/signup_spec.rb55
-rw-r--r--spec/features/todos/todos_spec.rb81
-rw-r--r--spec/finders/issues_finder_spec.rb16
-rw-r--r--spec/helpers/commits_helper_spec.rb29
-rw-r--r--spec/helpers/diff_helper_spec.rb20
-rw-r--r--spec/helpers/import_helper_spec.rb25
-rw-r--r--spec/helpers/projects_helper_spec.rb26
-rw-r--r--spec/javascripts/notes_spec.js.coffee1
-rw-r--r--spec/lib/award_emoji_spec.rb7
-rw-r--r--spec/lib/banzai/filter/external_link_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb42
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb343
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/issue_formatter_spec.rb40
-rw-r--r--spec/lib/gitlab/github_import/label_formatter_spec.rb19
-rw-r--r--spec/lib/gitlab/github_import/milestone_formatter_spec.rb82
-rw-r--r--spec/lib/gitlab/github_import/project_creator_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb58
-rw-r--r--spec/lib/gitlab/github_import/wiki_formatter_spec.rb7
-rw-r--r--spec/lib/gitlab/import_url_spec.rb21
-rw-r--r--spec/lib/gitlab/metrics/instrumentation_spec.rb51
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb3
-rw-r--r--spec/lib/gitlab/metrics_spec.rb24
-rw-r--r--spec/lib/gitlab/note_data_builder_spec.rb3
-rw-r--r--spec/lib/gitlab/push_data_builder_spec.rb15
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb150
-rw-r--r--spec/mailers/notify_spec.rb12
-rw-r--r--spec/mailers/repository_check_mailer_spec.rb21
-rw-r--r--spec/models/build_spec.rb16
-rw-r--r--spec/models/ci/commit_spec.rb96
-rw-r--r--spec/models/commit_spec.rb8
-rw-r--r--spec/models/commit_status_spec.rb50
-rw-r--r--spec/models/concerns/issuable_spec.rb30
-rw-r--r--spec/models/concerns/statuseable_spec.rb (renamed from spec/lib/ci/status_spec.rb)4
-rw-r--r--spec/models/event_spec.rb65
-rw-r--r--spec/models/external_issue_spec.rb15
-rw-r--r--spec/models/issue_spec.rb46
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb262
-rw-r--r--spec/models/project_services/builds_email_service_spec.rb37
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb4
-rw-r--r--spec/models/project_services/slack_service/merge_message_spec.rb4
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb2
-rw-r--r--spec/models/project_services/slack_service/wiki_page_message_spec.rb74
-rw-r--r--spec/models/project_services/slack_service_spec.rb17
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb251
-rw-r--r--spec/models/project_spec.rb22
-rw-r--r--spec/models/repository_spec.rb121
-rw-r--r--spec/requests/api/commits_spec.rb2
-rw-r--r--spec/requests/api/issues_spec.rb63
-rw-r--r--spec/requests/api/licenses_spec.rb136
-rw-r--r--spec/requests/api/merge_requests_spec.rb42
-rw-r--r--spec/requests/api/notes_spec.rb13
-rw-r--r--spec/requests/api/project_hooks_spec.rb14
-rw-r--r--spec/requests/api/projects_spec.rb48
-rw-r--r--spec/requests/api/tags_spec.rb2
-rw-r--r--spec/requests/api/users_spec.rb18
-rw-r--r--spec/requests/ci/api/builds_spec.rb46
-rw-r--r--spec/services/delete_tag_service_spec.rb11
-rw-r--r--spec/services/git_tag_push_service_spec.rb24
-rw-r--r--spec/services/issues/bulk_update_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb63
-rw-r--r--spec/services/issues/update_service_spec.rb11
-rw-r--r--spec/services/merge_requests/update_service_spec.rb11
-rw-r--r--spec/services/projects/transfer_service_spec.rb23
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml17
-rw-r--r--spec/support/project_hook_data_shared_example.rb17
-rw-r--r--spec/support/repo_helpers.rb2
-rw-r--r--spec/workers/post_receive_spec.rb43
-rw-r--r--spec/workers/repository_check/batch_worker_spec.rb39
-rw-r--r--spec/workers/repository_check/clear_worker_spec.rb17
-rw-r--r--spec/workers/repository_check/single_repository_worker_spec.rb34
111 files changed, 3853 insertions, 464 deletions
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 410b993fdfb..28cf804c1b2 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -12,13 +12,13 @@ describe AutocompleteController do
project.team << [user, :master]
end
- let(:body) { JSON.parse(response.body) }
-
describe 'GET #users with project ID' do
before do
get(:users, project_id: project.id)
end
+ let(:body) { JSON.parse(response.body) }
+
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq 1 }
it { expect(body.map { |u| u["username"] }).to include(user.username) }
@@ -143,4 +143,24 @@ describe AutocompleteController do
it { expect(body.size).to eq 0 }
end
end
+
+ context 'author of issuable included' do
+ before do
+ sign_in(user)
+ end
+
+ let(:body) { JSON.parse(response.body) }
+
+ it 'includes the author' do
+ get(:users, author_id: non_member.id)
+
+ expect(body.first["username"]).to eq non_member.username
+ end
+
+ it 'rejects non existent user ids' do
+ get(:users, author_id: 99999)
+
+ expect(body.collect { |u| u['id'] }).not_to include(99999)
+ end
+ end
end
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index f09e4fcb154..cf5c606c723 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -4,6 +4,8 @@ describe Projects::CommitController do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:commit) { project.commit("master") }
+ let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
+ let(:master_pickable_commit) { project.commit(master_pickable_sha) }
before do
sign_in(user)
@@ -192,4 +194,53 @@ describe Projects::CommitController do
end
end
end
+
+ describe '#cherry_pick' do
+ context 'when target branch is not provided' do
+ it 'should render the 404 page' do
+ post(:cherry_pick,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: master_pickable_commit.id)
+
+ expect(response).not_to be_success
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when the cherry-pick was successful' do
+ it 'should redirect to the commits page' do
+ post(:cherry_pick,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ target_branch: 'master',
+ id: master_pickable_commit.id)
+
+ expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
+ expect(flash[:notice]).to eq('The commit has been successfully cherry-picked.')
+ end
+ end
+
+ context 'when the cherry_pick failed' do
+ before do
+ post(:cherry_pick,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ target_branch: 'master',
+ id: master_pickable_commit.id)
+ end
+
+ it 'should redirect to the commit page' do
+ # Cherry-picking a commit that has been already cherry-picked.
+ post(:cherry_pick,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ target_branch: 'master',
+ id: master_pickable_commit.id)
+
+ expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+ expect(flash[:alert]).to match('Sorry, we cannot cherry-pick this commit automatically.')
+ end
+ end
+ end
end
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
new file mode 100644
index 00000000000..a5986598715
--- /dev/null
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Groups::GroupMembersController do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+
+ context "index" do
+ before do
+ group.add_owner(user)
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ it 'renders index with group members' do
+ get :index, group_id: group.path
+
+ expect(response.status).to eq(200)
+ expect(response).to render_template(:index)
+ end
+ end
+end
diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb
index b6573f105dc..3a82083717f 100644
--- a/spec/controllers/profiles/keys_controller_spec.rb
+++ b/spec/controllers/profiles/keys_controller_spec.rb
@@ -1,7 +1,17 @@
require 'spec_helper'
describe Profiles::KeysController do
- let(:user) { create(:user) }
+ let(:user) { create(:user) }
+
+ describe '#new' do
+ before { sign_in(user) }
+
+ it 'redirect to #index' do
+ get :new
+
+ expect(response).to redirect_to(profile_keys_path)
+ end
+ end
describe "#get_keys" do
describe "non existant user" do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
new file mode 100644
index 00000000000..40bd83af861
--- /dev/null
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Projects::GroupLinksController do
+ let(:project) { create(:project, :private) }
+ let(:group) { create(:group, :private) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ sign_in(user)
+ end
+
+ describe '#create' do
+ shared_context 'link project to group' do
+ before do
+ post(:create, namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ link_group_id: group.id,
+ link_group_access: ProjectGroupLink.default_access)
+ end
+ end
+
+ context 'when user has access to group he want to link project to' do
+ before { group.add_developer(user) }
+ include_context 'link project to group'
+
+ it 'links project with selected group' do
+ expect(group.shared_projects).to include project
+ end
+
+ it 'redirects to project group links page'do
+ expect(response).to redirect_to(
+ namespace_project_group_links_path(project.namespace, project)
+ )
+ end
+ end
+
+ context 'when user doers not have access to group he want to link to' do
+ include_context 'link project to group'
+
+ it 'renders 404' do
+ expect(response.status).to eq 404
+ end
+
+ it 'does not share project with that group' do
+ expect(group.shared_projects).to_not include project
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c54e83339a1..c0a1f45195f 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -300,14 +300,6 @@ describe Projects::MergeRequestsController do
expect(response.cookies['diff_view']).to eq('parallel')
end
-
- it 'assigns :view param based on cookie' do
- request.cookies['diff_view'] = 'parallel'
-
- go
-
- expect(controller.params[:view]).to eq 'parallel'
- end
end
describe 'GET commits' do
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index d47e4ab9a4f..ed64e7cf9af 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -46,4 +46,20 @@ describe Projects::ProjectMembersController do
end
end
end
+
+ describe '#index' do
+ let(:project) { create(:project, :private) }
+
+ context 'when user is member' do
+ let(:member) { create(:user) }
+
+ before do
+ project.team << [member, :guest]
+ sign_in(member)
+ get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
+ end
+
+ it { expect(response.status).to eq(200) }
+ end
+ end
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 7337ff58be1..8045c8b940d 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -33,7 +33,30 @@ describe UsersController do
it 'renders the show template' do
get :show, username: user.username
- expect(response).to be_success
+ expect(response.status).to eq(200)
+ expect(response).to render_template('show')
+ end
+ end
+ end
+
+ context 'when public visibility level is restricted' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ context 'when logged out' do
+ it 'renders 404' do
+ get :show, username: user.username
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'when logged in' do
+ before { sign_in(user) }
+
+ it 'renders show' do
+ get :show, username: user.username
+ expect(response.status).to eq(200)
expect(response).to render_template('show')
end
end
diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb
new file mode 100644
index 00000000000..ac6eb0a7897
--- /dev/null
+++ b/spec/factories/commits.rb
@@ -0,0 +1,12 @@
+require_relative '../support/repo_helpers'
+
+FactoryGirl.define do
+ factory :commit do
+ git_commit RepoHelpers.sample_commit
+ project factory: :empty_project
+
+ initialize_with do
+ new(git_commit, project)
+ end
+ end
+end
diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb
new file mode 100644
index 00000000000..7700b15d538
--- /dev/null
+++ b/spec/factories/oauth_access_tokens.rb
@@ -0,0 +1,22 @@
+# == Schema Information
+#
+# Table name: oauth_access_tokens
+#
+# id :integer not null, primary key
+# resource_owner_id :integer
+# application_id :integer
+# token :string not null
+# refresh_token :string
+# expires_in :integer
+# revoked_at :datetime
+# created_at :datetime not null
+# scopes :string
+#
+
+FactoryGirl.define do
+ factory :oauth_access_token do
+ resource_owner
+ application
+ token '123456'
+ end
+end
diff --git a/spec/factories/oauth_applications.rb b/spec/factories/oauth_applications.rb
new file mode 100644
index 00000000000..d116a573830
--- /dev/null
+++ b/spec/factories/oauth_applications.rb
@@ -0,0 +1,9 @@
+FactoryGirl.define do
+ factory :oauth_application, class: 'Doorkeeper::Application', aliases: [:application] do
+ name { FFaker::Name.name }
+ uid { FFaker::Name.name }
+ redirect_uri { FFaker::Internet.uri('http') }
+ owner
+ owner_type 'User'
+ end
+end
diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb
new file mode 100644
index 00000000000..a3403fd76ae
--- /dev/null
+++ b/spec/factories/project_wikis.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+ factory :project_wiki do
+ project factory: :empty_project
+ user factory: :user
+ initialize_with { new(project, user) }
+ end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index a5c60c51c5b..a9b2148bd2a 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -1,7 +1,7 @@
FactoryGirl.define do
sequence(:name) { FFaker::Name.name }
- factory :user, aliases: [:author, :assignee, :recipient, :owner, :creator] do
+ factory :user, aliases: [:author, :assignee, :recipient, :owner, :creator, :resource_owner] do
email { FFaker::Internet.email }
name
sequence(:username) { |n| "#{FFaker::Internet.user_name}#{n}" }
diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb
new file mode 100644
index 00000000000..938ccf2306b
--- /dev/null
+++ b/spec/factories/wiki_pages.rb
@@ -0,0 +1,9 @@
+require 'ostruct'
+
+FactoryGirl.define do
+ factory :wiki_page do
+ page = OpenStruct.new(url_path: 'some-name')
+ association :wiki, factory: :project_wiki, strategy: :build
+ initialize_with { new(wiki, page, true) }
+ end
+end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
new file mode 100644
index 00000000000..661fb761809
--- /dev/null
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+feature 'Admin uses repository checks', feature: true do
+ before { login_as :admin }
+
+ scenario 'to trigger a single check' do
+ project = create(:empty_project)
+ visit_admin_project_page(project)
+
+ page.within('.repository-check') do
+ click_button 'Trigger repository check'
+ end
+
+ expect(page).to have_content('Repository check was triggered')
+ end
+
+ scenario 'to see a single failed repository check' do
+ project = create(:empty_project)
+ project.update_columns(
+ last_repository_check_failed: true,
+ last_repository_check_at: Time.now,
+ )
+ visit_admin_project_page(project)
+
+ page.within('.alert') do
+ expect(page.text).to match(/Last repository check \(.* ago\) failed/)
+ end
+ end
+
+ scenario 'to clear all repository checks', js: true do
+ visit admin_application_settings_path
+
+ expect(RepositoryCheck::ClearWorker).to receive(:perform_async)
+
+ click_link 'Clear all repository checks'
+
+ expect(page).to have_content('Started asynchronous removal of all repository check states.')
+ end
+
+ def visit_admin_project_page(project)
+ visit admin_namespace_project_path(project.namespace, project)
+ end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 6da3a857b3f..090a941958f 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -86,6 +86,20 @@ describe "Builds" do
end
end
end
+
+ context 'Build raw trace' do
+ before do
+ @build.run!
+ @build.trace = 'BUILD TRACE'
+ visit namespace_project_build_path(@project.namespace, @project, @build)
+ end
+
+ it do
+ page.within('.build-controls') do
+ expect(page).to have_link 'Raw'
+ end
+ end
+ end
end
describe "POST /:project/builds/:id/cancel" do
@@ -120,4 +134,20 @@ describe "Builds" do
it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
end
+
+ describe "GET /:project/builds/:id/raw" do
+ before do
+ Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile')
+ @build.run!
+ @build.trace = 'BUILD TRACE'
+ visit namespace_project_build_path(@project.namespace, @project, @build)
+ end
+
+ it 'sends the right headers' do
+ page.within('.build-controls') { click_link 'Raw' }
+
+ expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8')
+ expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace)
+ end
+ end
end
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
new file mode 100644
index 00000000000..cf86e2c85e9
--- /dev/null
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe "Dashboard > User filters projects", feature: true do
+
+ describe 'filtering personal projects' do
+ before do
+ user = create(:user)
+ project = create(:project, name: "Victorialand", namespace: user.namespace)
+ project.team << [user, :master]
+
+ user2 = create(:user)
+ project2 = create(:project, name: "Treasure", namespace: user2.namespace)
+ project2.team << [user, :developer]
+
+ login_as(user)
+ visit dashboard_projects_path
+ end
+
+ it 'filters by projects "Owned by me"' do
+ click_link "Owned by me"
+
+ expect(page).to have_css('.is-active', text: 'Owned by me')
+ expect(page).to have_content('Victorialand')
+ expect(page).not_to have_content('Treasure')
+ end
+ end
+end
diff --git a/spec/features/issues/filter_by_labels_spec.rb b/spec/features/issues/filter_by_labels_spec.rb
new file mode 100644
index 00000000000..7f654684143
--- /dev/null
+++ b/spec/features/issues/filter_by_labels_spec.rb
@@ -0,0 +1,167 @@
+require 'rails_helper'
+
+feature 'Issue filtering by Labels', feature: true do
+ include WaitForAjax
+
+ let(:project) { create(:project, :public) }
+ let!(:user) { create(:user)}
+ let!(:label) { create(:label, project: project) }
+
+ before do
+ bug = create(:label, project: project, title: 'bug')
+ feature = create(:label, project: project, title: 'feature')
+ enhancement = create(:label, project: project, title: 'enhancement')
+
+ issue1 = create(:issue, title: "Bugfix1", project: project)
+ issue1.labels << bug
+
+ issue2 = create(:issue, title: "Bugfix2", project: project)
+ issue2.labels << bug
+ issue2.labels << enhancement
+
+ issue3 = create(:issue, title: "Feature1", project: project)
+ issue3.labels << feature
+
+ project.team << [user, :master]
+ login_as(user)
+
+ visit namespace_project_issues_path(project.namespace, project)
+ end
+
+ context 'filter by label bug', js: true do
+ before do
+ page.find('.js-label-select').click
+ wait_for_ajax
+ execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ wait_for_ajax
+ end
+
+ it 'should show issue "Bugfix1" and "Bugfix2" in issues list' do
+ expect(page).to have_content "Bugfix1"
+ expect(page).to have_content "Bugfix2"
+ end
+
+ it 'should not show "Feature1" in issues list' do
+ expect(page).not_to have_content "Feature1"
+ end
+
+ it 'should show label "bug" in filtered-labels' do
+ expect(find('.filtered-labels')).to have_content "bug"
+ end
+
+ it 'should not show label "feature" and "enhancement" in filtered-labels' do
+ expect(find('.filtered-labels')).not_to have_content "feature"
+ expect(find('.filtered-labels')).not_to have_content "enhancement"
+ end
+ end
+
+ context 'filter by label feature', js: true do
+ before do
+ page.find('.js-label-select').click
+ wait_for_ajax
+ execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ wait_for_ajax
+ end
+
+ it 'should show issue "Feature1" in issues list' do
+ expect(page).to have_content "Feature1"
+ end
+
+ it 'should not show "Bugfix1" and "Bugfix2" in issues list' do
+ expect(page).not_to have_content "Bugfix2"
+ expect(page).not_to have_content "Bugfix1"
+ end
+
+ it 'should show label "feature" in filtered-labels' do
+ expect(find('.filtered-labels')).to have_content "feature"
+ end
+
+ it 'should not show label "bug" and "enhancement" in filtered-labels' do
+ expect(find('.filtered-labels')).not_to have_content "bug"
+ expect(find('.filtered-labels')).not_to have_content "enhancement"
+ end
+ end
+
+ context 'filter by label enhancement', js: true do
+ before do
+ page.find('.js-label-select').click
+ wait_for_ajax
+ execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ wait_for_ajax
+ end
+
+ it 'should show issue "Bugfix2" in issues list' do
+ expect(page).to have_content "Bugfix2"
+ end
+
+ it 'should not show "Feature1" and "Bugfix1" in issues list' do
+ expect(page).not_to have_content "Feature1"
+ expect(page).not_to have_content "Bugfix1"
+ end
+
+ it 'should show label "enhancement" in filtered-labels' do
+ expect(find('.filtered-labels')).to have_content "enhancement"
+ end
+
+ it 'should not show label "feature" and "bug" in filtered-labels' do
+ expect(find('.filtered-labels')).not_to have_content "bug"
+ expect(find('.filtered-labels')).not_to have_content "feature"
+ end
+ end
+
+ context 'filter by label enhancement or feature', js: true do
+ before do
+ page.find('.js-label-select').click
+ wait_for_ajax
+ execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
+ execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ wait_for_ajax
+ end
+
+ it 'should not show "Bugfix1" or "Feature1" in issues list' do
+ expect(page).not_to have_content "Bugfix1"
+ expect(page).not_to have_content "Feature1"
+ end
+
+ it 'should show label "enhancement" and "feature" in filtered-labels' do
+ expect(find('.filtered-labels')).to have_content "enhancement"
+ expect(find('.filtered-labels')).to have_content "feature"
+ end
+
+ it 'should not show label "bug" in filtered-labels' do
+ expect(find('.filtered-labels')).not_to have_content "bug"
+ end
+ end
+
+ context 'filter by label enhancement and bug in issues list', js: true do
+ before do
+ page.find('.js-label-select').click
+ wait_for_ajax
+ execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
+ execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ wait_for_ajax
+ end
+
+ it 'should show issue "Bugfix2" in issues list' do
+ expect(page).to have_content "Bugfix2"
+ end
+
+ it 'should not show "Feature1"' do
+ expect(page).not_to have_content "Feature1"
+ end
+
+ it 'should show label "bug" and "enhancement" in filtered-labels' do
+ expect(find('.filtered-labels')).to have_content "bug"
+ expect(find('.filtered-labels')).to have_content "enhancement"
+ end
+
+ it 'should not show label "feature" in filtered-labels' do
+ expect(find('.filtered-labels')).not_to have_content "feature"
+ end
+ end
+end
diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb
index 69b22232f10..192e3619375 100644
--- a/spec/features/issues/filter_issues_spec.rb
+++ b/spec/features/issues/filter_issues_spec.rb
@@ -84,14 +84,20 @@ describe 'Filter issues', feature: true do
it 'should filter by any label' do
find('.dropdown-menu-labels a', text: 'Any Label').click
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ sleep 2
+
page.within '.labels-filter' do
expect(page).to have_content 'Any Label'
end
- expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Label')
+ expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Any Label')
end
it 'should filter by no label' do
find('.dropdown-menu-labels a', text: 'No Label').click
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
+ sleep 2
+
page.within '.labels-filter' do
expect(page).to have_content 'No Label'
end
@@ -121,6 +127,7 @@ describe 'Filter issues', feature: true do
find('.js-label-select').click
find('.dropdown-menu-labels .dropdown-content a', text: label.title).click
+ page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
sleep 2
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
new file mode 100644
index 00000000000..5739bc64dfb
--- /dev/null
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -0,0 +1,79 @@
+require 'rails_helper'
+
+feature 'Issue Sidebar', feature: true do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let!(:user) { create(:user)}
+
+ before do
+ create(:label, project: project, title: 'bug')
+ login_as(user)
+ end
+
+ context 'as a allowed user' do
+ before do
+ project.team << [user, :developer]
+ visit_issue(project, issue)
+ end
+
+ describe 'when clicking on edit labels', js: true do
+ it 'dropdown has an option to create a new label' do
+ find('.block.labels .edit-link').click
+
+ page.within('.block.labels') do
+ expect(page).to have_content 'Create new'
+ end
+ end
+ end
+
+ context 'creating a new label', js: true do
+ it 'option to crate a new label is present' do
+ page.within('.block.labels') do
+ find('.edit-link').click
+
+ expect(page).to have_content 'Create new'
+ end
+ end
+
+ it 'dropdown switches to "create label" section' do
+ page.within('.block.labels') do
+ find('.edit-link').click
+ click_link 'Create new'
+
+ expect(page).to have_content 'Create new label'
+ end
+ end
+
+ it 'new label is added' do
+ page.within('.block.labels') do
+ find('.edit-link').click
+ sleep 1
+ click_link 'Create new'
+
+ fill_in 'new_label_name', with: 'wontfix'
+ page.find(".suggest-colors a", match: :first).click
+ click_button 'Create'
+
+ page.within('.dropdown-page-one') do
+ expect(page).to have_content 'wontfix'
+ end
+ end
+ end
+ end
+ end
+
+ context 'as a guest' do
+ before do
+ project.team << [user, :guest]
+ visit_issue(project, issue)
+ end
+
+ it 'does not have a option to edit labels' do
+ expect(page).not_to have_selector('.block.labels .edit-link')
+ end
+ end
+
+ def visit_issue(project, issue)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 6fda0c31866..84c8e20ebaa 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -42,11 +42,9 @@ feature 'issue move to another project' do
expect(current_url).to include project_path(new_project)
- page.within('.issue') do
- expect(page).to have_content("Text with #{cross_reference}!1")
- expect(page).to have_content("Moved from #{cross_reference}#1")
- expect(page).to have_content(issue.title)
- end
+ expect(page).to have_content("Text with #{cross_reference}!1")
+ expect(page).to have_content("Moved from #{cross_reference}#1")
+ expect(page).to have_content(issue.title)
end
context 'projects user does not have permission to move issue to exist' do
@@ -74,7 +72,7 @@ feature 'issue move to another project' do
def edit_issue(issue)
visit issue_path(issue)
- page.within('.issuable-header') { click_link 'Edit' }
+ page.within('.issuable-actions') { first(:link, 'Edit').click }
end
def issue_path(issue)
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index 3eb903a93fe..b03dd0f666d 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -48,7 +48,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
click_update_issues_button
page.within('.issue .controls') do
- expect(find('.author_link')["data-original-title"]).to have_content(user.name)
+ expect(find('.author_link')["title"]).to have_content(user.name)
end
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 79000666ccc..b57131f68d5 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -45,7 +45,7 @@ describe 'Issues', feature: true do
project: project)
end
- it 'allows user to select unasigned', js: true do
+ it 'allows user to select unassigned', js: true do
visit edit_namespace_project_issue_path(project.namespace, project, issue)
expect(page).to have_content "Assignee #{@user.name}"
@@ -64,6 +64,18 @@ describe 'Issues', feature: true do
end
end
+ describe 'Issue info' do
+ it 'excludes award_emoji from comment count' do
+ issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
+ create(:upvote_note, noteable: issue)
+
+ visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
+
+ expect(page).to have_content 'foobar'
+ expect(page.all('.issue-no-comments').first.text).to eq "0"
+ end
+ end
+
describe 'Filter issue' do
before do
['foobar', 'barbaz', 'gitlab'].each do |title|
@@ -100,7 +112,7 @@ describe 'Issues', feature: true do
end
describe 'filter issue' do
- titles = ['foo','bar','baz']
+ titles = %w[foo bar baz]
titles.each_with_index do |title, index|
let!(title.to_sym) do
create(:issue, title: title,
@@ -141,8 +153,107 @@ describe 'Issues', feature: true do
expect(first_issue).to include('baz')
end
+ describe 'sorting by due date' do
+ before do
+ foo.update(due_date: 1.day.from_now)
+ bar.update(due_date: 6.days.from_now)
+ end
+
+ it 'sorts by recently due date' do
+ visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_soon)
+
+ expect(first_issue).to include('foo')
+ end
+
+ it 'sorts by least recently due date' do
+ visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later)
+
+ expect(first_issue).to include('bar')
+ end
+
+ it 'sorts by least recently due date by excluding nil due dates' do
+ bar.update(due_date: nil)
+
+ visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later)
+
+ expect(first_issue).to include('foo')
+ end
+
+ context 'with a filter on labels' do
+ let(:label) { create(:label, project: project) }
+ before { create(:label_link, label: label, target: foo) }
+
+ it 'sorts by least recently due date by excluding nil due dates' do
+ bar.update(due_date: nil)
+
+ visit namespace_project_issues_path(project.namespace, project, label_names: [label.name], sort: sort_value_due_date_later)
+
+ expect(first_issue).to include('foo')
+ end
+ end
+ end
+
+ describe 'filtering by due date' do
+ before do
+ foo.update(due_date: 1.day.from_now)
+ bar.update(due_date: 6.days.from_now)
+ end
+
+ it 'filters by none' do
+ visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NoDueDate.name)
+
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+
+ it 'filters by any' do
+ visit namespace_project_issues_path(project.namespace, project, due_date: Issue::AnyDueDate.name)
+
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+
+ it 'filters by due this week' do
+ foo.update(due_date: Date.today.beginning_of_week + 2.days)
+ bar.update(due_date: Date.today.end_of_week)
+ baz.update(due_date: Date.today - 8.days)
+
+ visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisWeek.name)
+
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
+
+ it 'filters by due this month' do
+ foo.update(due_date: Date.today.beginning_of_month + 2.days)
+ bar.update(due_date: Date.today.end_of_month)
+ baz.update(due_date: Date.today - 50.days)
+
+ visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisMonth.name)
+
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
+
+ it 'filters by overdue' do
+ foo.update(due_date: Date.today + 2.days)
+ bar.update(due_date: Date.today + 20.days)
+ baz.update(due_date: Date.yesterday)
+
+ visit namespace_project_issues_path(project.namespace, project, due_date: Issue::Overdue.name)
+
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+
describe 'sorting by milestone' do
- before :each do
+ before do
foo.milestone = newer_due_milestone
foo.save
bar.milestone = later_due_milestone
@@ -165,7 +276,7 @@ describe 'Issues', feature: true do
describe 'combine filter and sort' do
let(:user2) { create(:user) }
- before :each do
+ before do
foo.assignee = user2
foo.save
bar.assignee = user2
@@ -187,7 +298,7 @@ describe 'Issues', feature: true do
describe 'update assignee from issue#show' do
let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
- context 'by autorized user' do
+ context 'by authorized user' do
it 'allows user to select unassigned', js: true do
visit namespace_project_issue_path(project.namespace, project, issue)
@@ -206,13 +317,34 @@ describe 'Issues', feature: true do
expect(issue.reload.assignee).to be_nil
end
+
+ it 'allows user to select an assignee', js: true do
+ issue2 = create(:issue, project: project, author: @user)
+ visit namespace_project_issue_path(project.namespace, project, issue2)
+
+ page.within('.assignee') do
+ expect(page).to have_content "No assignee"
+ end
+
+ page.within '.assignee' do
+ click_link 'Edit'
+ end
+
+ page.within '.dropdown-menu-user' do
+ click_link @user.name
+ end
+
+ page.within('.assignee') do
+ expect(page).to have_content @user.name
+ end
+ end
end
context 'by unauthorized user' do
let(:guest) { create(:user) }
- before :each do
+ before do
project.team << [[guest], :guest]
end
@@ -255,7 +387,7 @@ describe 'Issues', feature: true do
context 'by unauthorized user' do
let(:guest) { create(:user) }
- before :each do
+ before do
project.team << [guest, :guest]
issue.milestone = milestone
issue.save
@@ -273,13 +405,30 @@ describe 'Issues', feature: true do
describe 'removing assignee' do
let(:user2) { create(:user) }
- before :each do
+ before do
issue.assignee = user2
issue.save
end
end
end
+ describe 'new issue' do
+ context 'dropzone upload file', js: true do
+ before do
+ visit new_namespace_project_issue_path(project.namespace, project)
+ end
+
+ it 'should upload file when dragging into textarea' do
+ drop_in_dropzone test_image_file
+
+ # Wait for the file to upload
+ sleep 1
+
+ expect(page.find_field("issue_description").value).to have_content 'banana_sample'
+ end
+ end
+ end
+
def first_issue
page.all('ul.issues-list > li').first.text
end
@@ -287,4 +436,25 @@ describe 'Issues', feature: true do
def last_issue
page.all('ul.issues-list > li').last.text
end
+
+ def drop_in_dropzone(file_path)
+ # Generate a fake input selector
+ page.execute_script <<-JS
+ var fakeFileInput = window.$('<input/>').attr(
+ {id: 'fakeFileInput', type: 'file'}
+ ).appendTo('body');
+ JS
+ # Attach the file to the fake input selector with Capybara
+ attach_file("fakeFileInput", file_path)
+ # Add the file to a fileList array and trigger the fake drop event
+ page.execute_script <<-JS
+ var fileList = [$('#fakeFileInput')[0].files[0]];
+ var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
+ $('.div-dropzone')[0].dropzone.listeners[0].events.drop(e);
+ JS
+ end
+
+ def test_image_file
+ File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
+ end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 3d0d0e59fd7..0148c87084a 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -165,7 +165,12 @@ describe 'GitLab Markdown', feature: true do
describe 'ExternalLinkFilter' do
it 'adds nofollow to external link' do
link = doc.at_css('a:contains("Google")')
- expect(link.attr('rel')).to match 'nofollow'
+ expect(link.attr('rel')).to include('nofollow')
+ end
+
+ it 'adds noreferrer to external link' do
+ link = doc.at_css('a:contains("Google")')
+ expect(link.attr('rel')).to include('noreferrer')
end
it 'ignores internal link' do
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb
new file mode 100644
index 00000000000..82bc5226d07
--- /dev/null
+++ b/spec/features/merge_requests/cherry_pick_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe 'Cherry-pick Merge Requests' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
+
+ before do
+ login_as user
+ project.team << [user, :master]
+ end
+
+ context "Viewing a merged merge request" do
+ before do
+ service = MergeRequests::MergeService.new(project, user)
+
+ perform_enqueued_jobs do
+ service.execute(merge_request)
+ end
+ end
+
+ # Fast-forward merge, or merged before GitLab 8.5.
+ context "Without a merge commit" do
+ before do
+ merge_request.merge_commit_sha = nil
+ merge_request.save
+ end
+
+ it "doesn't show a Cherry-pick button" do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+ expect(page).not_to have_link "Cherry-pick"
+ end
+ end
+
+ context "With a merge commit" do
+ it "shows a Cherry-pick button" do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+ expect(page).to have_link "Cherry-pick"
+ end
+ end
+ 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 c57ab5f3b03..e3ecd60a5f3 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -2,8 +2,14 @@ require 'rails_helper'
feature 'Merge Request filtering by Milestone', feature: true do
let(:project) { create(:project, :public) }
+ let!(:user) { create(:user)}
let(:milestone) { create(:milestone, project: project) }
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
scenario 'filters by no Milestone', js: true do
create(:merge_request, :with_diffs, source_project: project)
create(:merge_request, :simple, source_project: project, milestone: milestone)
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 5f855ccc701..389812ff7e1 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -4,6 +4,20 @@ describe 'Comments', feature: true do
include RepoHelpers
include WaitForAjax
+ describe 'On merge requests page', feature: true do
+ it 'excludes award_emoji from comment count' do
+ merge_request = create(:merge_request)
+ project = merge_request.source_project
+ create(:upvote_note, noteable: merge_request, project: project)
+
+ login_as :admin
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ expect(merge_request.mr_and_commit_notes.count).to eq 1
+ expect(page.all('.merge-request-no-comments').first.text).to eq "0"
+ end
+ end
+
describe 'On a merge request', js: true, feature: true do
let!(:merge_request) { create(:merge_request) }
let!(:project) { merge_request.source_project }
@@ -129,6 +143,17 @@ describe 'Comments', feature: true do
end
end
end
+
+ describe 'comment info' do
+ it 'excludes award_emoji from comment count' do
+ create(:upvote_note, noteable: merge_request, project: project)
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+
+ expect(merge_request.mr_and_commit_notes.count).to eq 2
+ expect(find('.notes-tab span.badge').text).to eq "1"
+ end
+ end
end
describe 'On a merge request diff', js: true, feature: true do
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
new file mode 100644
index 00000000000..1adab7e9c6c
--- /dev/null
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -0,0 +1,98 @@
+require 'spec_helper'
+
+feature 'Member autocomplete', feature: true do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:participant) { create(:user) }
+ let(:author) { create(:user) }
+
+ before do
+ allow_any_instance_of(Commit).to receive(:author).and_return(author)
+ login_as user
+ end
+
+ shared_examples "open suggestions" do
+ it 'suggestions are displayed' do
+ expect(page).to have_selector('.atwho-view', visible: true)
+ end
+
+ it 'author is suggested' do
+ page.within('.atwho-view', visible: true) do
+ expect(page).to have_content(author.username)
+ end
+ end
+
+ it 'participant is suggested' do
+ page.within('.atwho-view', visible: true) do
+ expect(page).to have_content(participant.username)
+ end
+ end
+ end
+
+ context 'adding a new note on a Issue', js: true do
+ before do
+ issue = create(:issue, author: author, project: project)
+ create(:note, note: 'Ultralight Beam', noteable: issue, author: participant)
+ visit_issue(project, issue)
+ end
+
+ context 'when typing @' do
+ include_examples "open suggestions"
+ before do
+ open_member_suggestions
+ end
+ end
+ end
+
+ context 'adding a new note on a Merge Request ', js: true do
+ before do
+ merge = create(:merge_request, source_project: project, target_project: project, author: author)
+ create(:note, note: 'Ultralight Beam', noteable: merge, author: participant)
+ visit_merge_request(project, merge)
+ end
+
+ context 'when typing @' do
+ include_examples "open suggestions"
+ before do
+ open_member_suggestions
+ end
+ end
+ end
+
+ context 'adding a new note on a Commit ', js: true do
+ let(:commit) { project.commit }
+
+ before do
+ allow(commit).to receive(:author).and_return(author)
+ create(:note_on_commit, author: participant, project: project, commit_id: project.repository.commit.id, note: 'No More Parties in LA')
+ visit_commit(project, commit)
+ end
+
+ context 'when typing @' do
+ include_examples "open suggestions"
+ before do
+ open_member_suggestions
+ end
+ end
+ end
+
+ def open_member_suggestions
+ sleep 1
+ page.within('.new-note') do
+ sleep 1
+ find('#note_note').native.send_keys('@')
+ end
+ end
+
+ def visit_issue(project, issue)
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ def visit_merge_request(project, merge)
+ visit namespace_project_merge_request_path(project.namespace, project, merge)
+ end
+
+ def visit_commit(project, commit)
+ visit namespace_project_commit_path(project.namespace, project, commit)
+ end
+end
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
new file mode 100644
index 00000000000..1a5a9059dbd
--- /dev/null
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe 'Profile > Applications', feature: true do
+ let(:user) { create(:user) }
+
+ before do
+ login_as(user)
+ end
+
+ describe 'User manages applications', js: true do
+ it 'deletes an application' do
+ create(:oauth_application, owner: user)
+ visit oauth_applications_path
+
+ page.within('.oauth-applications') do
+ expect(page).to have_content('Your applications (1)')
+ click_button 'Destroy'
+ end
+
+ expect(page).to have_content('The application was deleted successfully')
+ expect(page).to have_content('Your applications (0)')
+ expect(page).to have_content('Authorized applications (0)')
+ end
+
+ it 'deletes an authorized application' do
+ create(:oauth_access_token, resource_owner: user)
+ visit oauth_applications_path
+
+ page.within('.oauth-authorized-applications') do
+ expect(page).to have_content('Authorized applications (1)')
+ click_button 'Revoke'
+ end
+
+ expect(page).to have_content('The application was revoked access.')
+ expect(page).to have_content('Your applications (0)')
+ expect(page).to have_content('Authorized applications (0)')
+ end
+ end
+end
diff --git a/spec/features/project/shortcuts_spec.rb b/spec/features/project/shortcuts_spec.rb
new file mode 100644
index 00000000000..2595c4181e5
--- /dev/null
+++ b/spec/features/project/shortcuts_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+feature 'Project shortcuts', feature: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ describe 'On a project', js: true do
+ before do
+ project.team << [user, :master]
+ login_as user
+ visit namespace_project_path(project.namespace, project)
+ end
+
+ describe 'pressing "i"' do
+ it 'redirects to new issue page' do
+ find('body').native.send_key('i')
+ expect(page).to have_content('New Issue')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
new file mode 100644
index 00000000000..40ba0bdc115
--- /dev/null
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+feature 'project commit builds' do
+ given(:project) { create(:project) }
+
+ background do
+ user = create(:user)
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ context 'when no builds triggered yet' do
+ background do
+ create(:ci_commit, project: project,
+ sha: project.commit.sha,
+ ref: 'master')
+ end
+
+ scenario 'user views commit builds page' do
+ visit builds_namespace_project_commit_path(project.namespace,
+ project, project.commit.sha)
+
+
+ expect(page).to have_content('Builds')
+ end
+ end
+end
diff --git a/spec/features/projects/commits/cherry_pick_spec.rb b/spec/features/projects/commits/cherry_pick_spec.rb
new file mode 100644
index 00000000000..0559b02f321
--- /dev/null
+++ b/spec/features/projects/commits/cherry_pick_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe 'Cherry-pick Commits' do
+ let(:project) { create(:project) }
+ let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
+ let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
+
+
+ before do
+ login_as :user
+ project.team << [@user, :master]
+ visit namespace_project_commits_path(project.namespace, project, project.repository.root_ref, { limit: 5 })
+ end
+
+ context "I cherry-pick a commit" do
+ it do
+ visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+ find("a[href='#modal-cherry-pick-commit']").click
+ page.within('#modal-cherry-pick-commit') do
+ uncheck 'create_merge_request'
+ click_button 'Cherry-pick'
+ end
+ expect(page).to have_content('The commit has been successfully cherry-picked.')
+ end
+ end
+
+ context "I cherry-pick a merge commit" do
+ it do
+ visit namespace_project_commit_path(project.namespace, project, master_pickable_merge.id)
+ find("a[href='#modal-cherry-pick-commit']").click
+ page.within('#modal-cherry-pick-commit') do
+ uncheck 'create_merge_request'
+ click_button 'Cherry-pick'
+ end
+ expect(page).to have_content('The commit has been successfully cherry-picked.')
+ end
+ end
+
+ context "I cherry-pick a commit that was previously cherry-picked" do
+ it do
+ visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+ find("a[href='#modal-cherry-pick-commit']").click
+ page.within('#modal-cherry-pick-commit') do
+ uncheck 'create_merge_request'
+ click_button 'Cherry-pick'
+ end
+ visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+ find("a[href='#modal-cherry-pick-commit']").click
+ page.within('#modal-cherry-pick-commit') do
+ uncheck 'create_merge_request'
+ click_button 'Cherry-pick'
+ end
+ expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.')
+ end
+ end
+
+ context "I cherry-pick a commit in a new merge request" do
+ it do
+ visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
+ find("a[href='#modal-cherry-pick-commit']").click
+ page.within('#modal-cherry-pick-commit') do
+ click_button 'Cherry-pick'
+ end
+ expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.')
+ end
+ end
+end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
new file mode 100644
index 00000000000..3d6ffbc4c6b
--- /dev/null
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+feature 'project owner creates a license file', feature: true, js: true do
+ include Select2Helper
+
+ let(:project_master) { create(:user) }
+ let(:project) { create(:project) }
+ background do
+ project.repository.remove_file(project_master, 'LICENSE', 'Remove LICENSE', 'master')
+ project.team << [project_master, :master]
+ login_as(project_master)
+ visit namespace_project_path(project.namespace, project)
+ end
+
+ scenario 'project master creates a license file manually from a template' do
+ visit namespace_project_tree_path(project.namespace, project, project.repository.root_ref)
+ find('.add-to-tree').click
+ click_link 'New file'
+
+ fill_in :file_name, with: 'LICENSE'
+
+ expect(page).to have_selector('.license-selector')
+
+ select2('mit', from: '#license_type')
+
+ file_content = find('.file-content')
+ expect(file_content).to have_content('The MIT License (MIT)')
+ expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+
+ fill_in :commit_message, with: 'Add a LICENSE file', visible: true
+ click_button 'Commit Changes'
+
+ expect(current_path).to eq(
+ namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
+ expect(page).to have_content('The MIT License (MIT)')
+ expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ end
+
+ scenario 'project master creates a license file from the "Add license" link' do
+ click_link 'Add License'
+
+ expect(current_path).to eq(
+ namespace_project_new_blob_path(project.namespace, project, 'master'))
+ expect(find('#file_name').value).to eq('LICENSE')
+ expect(page).to have_selector('.license-selector')
+
+ select2('mit', from: '#license_type')
+
+ file_content = find('.file-content')
+ expect(file_content).to have_content('The MIT License (MIT)')
+ expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+
+ fill_in :commit_message, with: 'Add a LICENSE file', visible: true
+ click_button 'Commit Changes'
+
+ expect(current_path).to eq(
+ namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
+ expect(page).to have_content('The MIT License (MIT)')
+ expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ end
+end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
new file mode 100644
index 00000000000..3268e240200
--- /dev/null
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+feature 'project owner sees a link to create a license file in empty project', feature: true, js: true do
+ include Select2Helper
+
+ let(:project_master) { create(:user) }
+ let(:project) { create(:empty_project) }
+ background do
+ project.team << [project_master, :master]
+ login_as(project_master)
+ end
+
+ scenario 'project master creates a license file from a template' do
+ visit namespace_project_path(project.namespace, project)
+ click_link 'Create empty bare repository'
+ click_on 'LICENSE'
+
+ expect(current_path).to eq(
+ namespace_project_new_blob_path(project.namespace, project, 'master'))
+ expect(find('#file_name').value).to eq('LICENSE')
+ expect(page).to have_selector('.license-selector')
+
+ select2('mit', from: '#license_type')
+
+ file_content = find('.file-content')
+ expect(file_content).to have_content('The MIT License (MIT)')
+ expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+
+ fill_in :commit_message, with: 'Add a LICENSE file', visible: true
+ # Remove pre-receive hook so we can push without auth
+ FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive'))
+ click_button 'Commit Changes'
+
+ expect(current_path).to eq(
+ namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
+ expect(page).to have_content('The MIT License (MIT)')
+ expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ end
+end
diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
new file mode 100644
index 00000000000..c5e3d143d91
--- /dev/null
+++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Anonymous user sees members', feature: true do
+ let(:user) { create(:user) }
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:empty_project, :public) }
+
+ background do
+ project.team << [user, :master]
+ create(:project_group_link, project: project, group: group)
+ end
+
+ scenario "anonymous user visits the project's members page and sees the list of members" do
+ visit namespace_project_project_members_path(project.namespace, project)
+
+ expect(current_path).to eq(
+ namespace_project_project_members_path(project.namespace, project))
+ expect(page).to have_content(user.name)
+ end
+end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index e8886e7edf9..8edeb8d18af 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -80,6 +80,22 @@ describe "Runners" do
end
end
+ describe "shared runners description" do
+ let(:shared_runners_text) { 'custom **shared** runners description' }
+ let(:shared_runners_html) { 'custom shared runners description' }
+
+ before do
+ stub_application_setting(shared_runners_text: shared_runners_text)
+ project = FactoryGirl.create :empty_project, shared_runners_enabled: false
+ project.team << [user, :master]
+ visit runners_path(project)
+ end
+
+ it "sees shared runners description" do
+ expect(page.find(".shared-runners-description")).to have_content(shared_runners_html)
+ end
+ end
+
describe "show page" do
before do
@project = FactoryGirl.create :empty_project
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 3e6289a46b1..029a11ea43c 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -10,6 +10,10 @@ describe "Search", feature: true do
visit search_path
end
+ it 'top right search form is not present' do
+ expect(page).not_to have_selector('.search')
+ end
+
describe 'searching for Projects' do
it 'finds a project' do
page.within '.search-holder' do
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 79d5bf4cf06..8625ea6bc10 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -101,12 +101,12 @@ describe "Internal Project Access", feature: true do
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_denied_for developer }
- it { is_expected.to be_denied_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_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 :visitor }
+ it { is_expected.to be_denied_for :external }
end
describe "GET /:project_path/blob" do
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index 0a89193eb67..544270b4037 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -101,9 +101,9 @@ describe "Private Project Access", feature: true do
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_denied_for developer }
- it { is_expected.to be_denied_for reporter }
- it { is_expected.to be_denied_for guest }
+ 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 }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 40daac89d40..4def4f99bc0 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -101,12 +101,12 @@ describe "Public Project Access", feature: true do
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_denied_for developer }
- it { is_expected.to be_denied_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 }
+ 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 :visitor }
+ it { is_expected.to be_allowed_for :external }
end
describe "GET /:project_path/builds" do
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
new file mode 100644
index 00000000000..51b754ff85c
--- /dev/null
+++ b/spec/features/signup_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+feature 'Signup', feature: true do
+ describe 'signup with no errors' do
+ it 'creates the user account and sends a confirmation email' do
+ user = build(:user)
+
+ visit root_path
+
+ fill_in 'user_name', with: user.name
+ fill_in 'user_username', with: user.username
+ fill_in 'user_email', with: user.email
+ fill_in 'user_password_sign_up', with: user.password
+ click_button "Sign up"
+
+ expect(current_path).to eq users_almost_there_path
+ expect(page).to have_content("Please check your email to confirm your account")
+ end
+ end
+
+ describe 'signup with errors' do
+ it "displays the errors" do
+ existing_user = create(:user)
+ user = build(:user)
+
+ visit root_path
+
+ fill_in 'user_name', with: user.name
+ fill_in 'user_username', with: user.username
+ fill_in 'user_email', with: existing_user.email
+ fill_in 'user_password_sign_up', with: user.password
+ click_button "Sign up"
+
+ expect(current_path).to eq user_registration_path
+ expect(page).to have_content("error prohibited this user from being saved")
+ expect(page).to have_content("Email has already been taken")
+ end
+
+ it 'does not redisplay the password' do
+ existing_user = create(:user)
+ user = build(:user)
+
+ visit root_path
+
+ fill_in 'user_name', with: user.name
+ fill_in 'user_username', with: user.username
+ fill_in 'user_email', with: existing_user.email
+ fill_in 'user_password_sign_up', with: user.password
+ click_button "Sign up"
+
+ expect(current_path).to eq user_registration_path
+ expect(page.body).not_to match(/#{user.password}/)
+ end
+ end
+end
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
new file mode 100644
index 00000000000..248e004ba6e
--- /dev/null
+++ b/spec/features/todos/todos_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+describe 'Dashboard Todos', feature: true do
+ let(:user) { create(:user) }
+ let(:author) { create(:user) }
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue) }
+
+ describe 'GET /dashboard/todos' do
+ context 'User does not have todos' do
+ before do
+ login_as(user)
+ visit dashboard_todos_path
+ end
+ it 'shows "All done" message' do
+ expect(page).to have_content "You're all done!"
+ end
+ end
+
+ context 'User has a todo', js: true do
+ before do
+ create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
+ login_as(user)
+ visit dashboard_todos_path
+ end
+
+ it 'todo is present' do
+ expect(page).to have_selector('.todos-list .todo', count: 1)
+ end
+
+ describe 'deleting the todo' do
+ before do
+ first('.done-todo').click
+ end
+
+ it 'is removed from the list' do
+ expect(page).not_to have_selector('.todos-list .todo')
+ end
+
+ it 'shows "All done" message' do
+ expect(page).to have_content("You're all done!")
+ end
+ end
+ end
+
+ context 'User has multiple pages of Todos' do
+ before do
+ allow(Todo).to receive(:default_per_page).and_return(1)
+
+ # Create just enough records to cause us to paginate
+ create_list(:todo, 2, :mentioned, user: user, project: project, target: issue, author: author)
+
+ login_as(user)
+ end
+
+ it 'is paginated' do
+ visit dashboard_todos_path
+
+ expect(page).to have_selector('.gl-pagination')
+ end
+
+ it 'is has the right number of pages' do
+ visit dashboard_todos_path
+
+ expect(page).to have_selector('.gl-pagination .page', count: 2)
+ end
+
+ describe 'completing last todo from last page', js: true do
+ it 'redirects to the previous page' do
+ visit dashboard_todos_path(page: 2)
+ expect(page).to have_content(Todo.first.body)
+
+ click_link('Done')
+
+ expect(current_path).to eq dashboard_todos_path
+ expect(page).to have_content(Todo.last.body)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index b1648055462..bc607a29751 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -62,6 +62,22 @@ describe IssuesFinder do
expect(issues).to eq([issue2])
end
+ it 'returns unique issues when filtering by multiple labels' do
+ label2 = create(:label, project: project2)
+
+ create(:label_link, label: label2, target: issue2)
+
+ params = {
+ scope: 'all',
+ label_name: [label.title, label2.title].join(','),
+ state: 'opened'
+ }
+
+ issues = IssuesFinder.new(user, params).execute
+
+ expect(issues).to eq([issue2])
+ end
+
it 'should filter by no label name' do
params = { scope: "all", label_name: Label::None.title, state: 'opened' }
issues = IssuesFinder.new(user, params).execute
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
new file mode 100644
index 00000000000..727c25ff529
--- /dev/null
+++ b/spec/helpers/commits_helper_spec.rb
@@ -0,0 +1,29 @@
+require 'rails_helper'
+
+describe CommitsHelper do
+ describe 'commit_author_link' do
+ it 'escapes the author email' do
+ commit = double(
+ author: nil,
+ author_name: 'Persistent XSS',
+ author_email: 'my@email.com" onmouseover="alert(1)'
+ )
+
+ expect(helper.commit_author_link(commit)).
+ not_to include('onmouseover="alert(1)"')
+ end
+ end
+
+ describe 'commit_committer_link' do
+ it 'escapes the committer email' do
+ commit = double(
+ committer: nil,
+ committer_name: 'Persistent XSS',
+ committer_email: 'my@email.com" onmouseover="alert(1)'
+ )
+
+ expect(helper.commit_committer_link(commit)).
+ not_to include('onmouseover="alert(1)"')
+ end
+ end
+end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 982c113e84b..b7810185d16 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -11,6 +11,26 @@ describe DiffHelper do
let(:diff_refs) { [commit.parent, commit] }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs) }
+ describe 'diff_view' do
+ it 'returns a valid value when cookie is set' do
+ helper.request.cookies[:diff_view] = 'parallel'
+
+ expect(helper.diff_view).to eq 'parallel'
+ end
+
+ it 'returns a default value when cookie is invalid' do
+ helper.request.cookies[:diff_view] = 'invalid'
+
+ expect(helper.diff_view).to eq 'inline'
+ end
+
+ it 'returns a default value when cookie is nil' do
+ expect(helper.request.cookies).to be_empty
+
+ expect(helper.diff_view).to eq 'inline'
+ end
+ end
+
describe 'diff_hard_limit_enabled?' do
it 'should return true if param is provided' do
allow(controller).to receive(:params) { { force_show_diff: true } }
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
new file mode 100644
index 00000000000..3391234e9f5
--- /dev/null
+++ b/spec/helpers/import_helper_spec.rb
@@ -0,0 +1,25 @@
+require 'rails_helper'
+
+describe ImportHelper do
+ describe '#github_project_link' do
+ context 'when provider does not specify a custom URL' do
+ it 'uses default GitHub URL' do
+ allow(Gitlab.config.omniauth).to receive(:providers).
+ and_return([Settingslogic.new('name' => 'github')])
+
+ expect(helper.github_project_link('octocat/Hello-World')).
+ to include('href="https://github.com/octocat/Hello-World"')
+ end
+ end
+
+ context 'when provider specify a custom URL' do
+ it 'uses custom URL' do
+ allow(Gitlab.config.omniauth).to receive(:providers).
+ and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
+
+ expect(helper.github_project_link('octocat/Hello-World')).
+ to include('href="https://github.company.com/octocat/Hello-World"')
+ end
+ end
+ end
+end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index c258cfebd73..62389188d2c 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -105,4 +105,30 @@ describe ProjectsHelper do
end
end
end
+
+ describe '#license_short_name' do
+ let(:project) { create(:project) }
+
+ context 'when project.repository has a license_key' do
+ it 'returns the nickname of the license if present' do
+ allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
+
+ expect(helper.license_short_name(project)).to eq('GNU AGPLv3')
+ end
+
+ it 'returns the name of the license if nickname is not present' do
+ allow(project.repository).to receive(:license_key).and_return('mit')
+
+ expect(helper.license_short_name(project)).to eq('MIT License')
+ end
+ end
+
+ context 'when project.repository has no license_key but a license_blob' do
+ it 'returns LICENSE' do
+ allow(project.repository).to receive(:license_key).and_return(nil)
+
+ expect(helper.license_short_name(project)).to eq('LICENSE')
+ end
+ end
+ end
end
diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee
index 050b6e362c6..dd160e821b3 100644
--- a/spec/javascripts/notes_spec.js.coffee
+++ b/spec/javascripts/notes_spec.js.coffee
@@ -1,4 +1,5 @@
#= require notes
+#= require gl_form
window.gon = {}
window.disableButtonIfEmptyField = -> null
diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb
index 330678f7f16..88c22912950 100644
--- a/spec/lib/award_emoji_spec.rb
+++ b/spec/lib/award_emoji_spec.rb
@@ -16,4 +16,11 @@ describe AwardEmoji do
end
end
end
+
+ describe '.emoji_by_category' do
+ it "only contains known categories" do
+ undefined_categories = AwardEmoji.emoji_by_category.keys - AwardEmoji::CATEGORIES.keys
+ expect(undefined_categories).to be_empty
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb
index e3a8e15330e..f4c5c621bd0 100644
--- a/spec/lib/banzai/filter/external_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_link_filter_spec.rb
@@ -24,6 +24,14 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do
doc = filter(act)
expect(doc.at_css('a')).to have_attribute('rel')
- expect(doc.at_css('a')['rel']).to eq 'nofollow'
+ expect(doc.at_css('a')['rel']).to include 'nofollow'
+ end
+
+ it 'adds rel="noreferrer" to external links' do
+ act = %q(<a href="https://google.com/">Google</a>)
+ doc = filter(act)
+
+ expect(doc.at_css('a')).to have_attribute('rel')
+ expect(doc.at_css('a')['rel']).to include 'noreferrer'
end
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 94468abcbb3..b0a38e7c251 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -178,27 +178,37 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
describe 'cross project label references' do
- let(:another_project) { create(:empty_project, :public) }
- let(:project_name) { another_project.name_with_namespace }
- let(:label) { create(:label, project: another_project, color: '#00ff00') }
- let(:reference) { label.to_reference(project) }
+ context 'valid project referenced' do
+ let(:another_project) { create(:empty_project, :public) }
+ let(:project_name) { another_project.name_with_namespace }
+ let(:label) { create(:label, project: another_project, color: '#00ff00') }
+ let(:reference) { label.to_reference(project) }
- let!(:result) { reference_filter("See #{reference}") }
+ let!(:result) { reference_filter("See #{reference}") }
- it 'points to referenced project issues page' do
- expect(result.css('a').first.attr('href'))
- .to eq urls.namespace_project_issues_url(another_project.namespace,
- another_project,
- label_name: label.name)
- end
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(another_project.namespace,
+ another_project,
+ label_name: label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
+ end
- it 'has valid color' do
- expect(result.css('a span').first.attr('style'))
- .to match /background-color: #00ff00/
+ it 'contains cross project content' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
+ end
end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
+ context 'project that does not exist referenced' do
+ let(:result) { reference_filter('aaa/bbb~ccc') }
+
+ it 'does not link reference' do
+ expect(result.to_html).to eq 'aaa/bbb~ccc'
+ end
end
end
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index dcb8a3451bd..c7ab3185378 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -286,6 +286,81 @@ module Ci
end
end
+
+ describe "Scripts handling" do
+ let(:config_data) { YAML.dump(config) }
+ let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) }
+
+ subject { config_processor.builds_for_stage_and_ref("test", "master").first }
+
+ describe "before_script" do
+ context "in global context" do
+ let(:config) do
+ {
+ before_script: ["global script"],
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return commands with scripts concencaced" do
+ expect(subject[:commands]).to eq("global script\nscript")
+ end
+ end
+
+ context "overwritten in local context" do
+ let(:config) do
+ {
+ before_script: ["global script"],
+ test: { before_script: ["local script"], script: ["script"] }
+ }
+ end
+
+ it "return commands with scripts concencaced" do
+ expect(subject[:commands]).to eq("local script\nscript")
+ end
+ end
+ end
+
+ describe "script" do
+ let(:config) do
+ {
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return commands with scripts concencaced" do
+ expect(subject[:commands]).to eq("script")
+ end
+ end
+
+ describe "after_script" do
+ context "in global context" do
+ let(:config) do
+ {
+ after_script: ["after_script"],
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return after_script in options" do
+ expect(subject[:options][:after_script]).to eq(["after_script"])
+ end
+ end
+
+ context "overwritten in local context" do
+ let(:config) do
+ {
+ after_script: ["local after_script"],
+ test: { after_script: ["local after_script"], script: ["script"] }
+ }
+ end
+
+ it "return after_script in options" do
+ expect(subject[:options][:after_script]).to eq(["local after_script"])
+ end
+ end
+ end
+ end
describe "Image and service handling" do
it "returns image and service when defined" do
@@ -345,20 +420,76 @@ module Ci
end
end
- describe "Variables" do
- it "returns variables when defined" do
- variables = {
- var1: "value1",
- var2: "value2",
- }
- config = YAML.dump({
- variables: variables,
- before_script: ["pwd"],
- rspec: { script: "rspec" }
- })
+ describe 'Variables' do
+ context 'when global variables are defined' do
+ it 'returns global variables' do
+ variables = {
+ VAR1: 'value1',
+ VAR2: 'value2',
+ }
- config_processor = GitlabCiYamlProcessor.new(config, path)
- expect(config_processor.variables).to eq(variables)
+ config = YAML.dump({
+ variables: variables,
+ before_script: ['pwd'],
+ rspec: { script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.global_variables).to eq(variables)
+ end
+ end
+
+ context 'when job variables are defined' do
+ context 'when syntax is correct' do
+ it 'returns job variables' do
+ variables = {
+ KEY1: 'value1',
+ SOME_KEY_2: 'value2'
+ }
+
+ config = YAML.dump(
+ { before_script: ['pwd'],
+ rspec: {
+ variables: variables,
+ script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.job_variables(:rspec)).to eq variables
+ end
+ end
+
+ context 'when syntax is incorrect' do
+ it 'raises error' do
+ variables = [:KEY1, 'value1', :KEY2, 'value2']
+
+ config = YAML.dump(
+ { before_script: ['pwd'],
+ rspec: {
+ variables: variables,
+ script: 'rspec' }
+ })
+
+ expect { GitlabCiYamlProcessor.new(config, path) }
+ .to raise_error(GitlabCiYamlProcessor::ValidationError,
+ /job: variables should be a map/)
+ end
+ end
+ end
+
+ context 'when job variables are not defined' do
+ it 'returns empty array' do
+ config = YAML.dump({
+ before_script: ['pwd'],
+ rspec: { script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.job_variables(:rspec)).to eq []
+ end
end
end
@@ -517,70 +648,131 @@ module Ci
end
describe "Hidden jobs" do
- let(:config) do
- YAML.dump({
- '.hidden_job' => { script: 'test' },
- 'normal_job' => { script: 'test' }
- })
+ let(:config_processor) { GitlabCiYamlProcessor.new(config) }
+ subject { config_processor.builds_for_stage_and_ref("test", "master") }
+
+ shared_examples 'hidden_job_handling' do
+ it "doesn't create jobs that start with dot" do
+ expect(subject.size).to eq(1)
+ expect(subject.first).to eq({
+ except: nil,
+ stage: "test",
+ stage_idx: 1,
+ name: :normal_job,
+ only: nil,
+ commands: "test",
+ tag_list: [],
+ options: {},
+ when: "on_success",
+ allow_failure: false
+ })
+ end
end
- let(:config_processor) { GitlabCiYamlProcessor.new(config) }
+ context 'when hidden job have a script definition' do
+ let(:config) do
+ YAML.dump({
+ '.hidden_job' => { image: 'ruby:2.1', script: 'test' },
+ 'normal_job' => { script: 'test' }
+ })
+ end
- subject { config_processor.builds_for_stage_and_ref("test", "master") }
+ it_behaves_like 'hidden_job_handling'
+ end
- it "doesn't create jobs that starts with dot" do
- expect(subject.size).to eq(1)
- expect(subject.first).to eq({
- except: nil,
- stage: "test",
- stage_idx: 1,
- name: :normal_job,
- only: nil,
- commands: "\ntest",
- tag_list: [],
- options: {},
- when: "on_success",
- allow_failure: false
- })
+ context "when hidden job doesn't have a script definition" do
+ let(:config) do
+ YAML.dump({
+ '.hidden_job' => { image: 'ruby:2.1' },
+ 'normal_job' => { script: 'test' }
+ })
+ end
+
+ it_behaves_like 'hidden_job_handling'
end
end
describe "YAML Alias/Anchor" do
- it "is correctly supported for jobs" do
- config = <<EOT
+ let(:config_processor) { GitlabCiYamlProcessor.new(config) }
+ subject { config_processor.builds_for_stage_and_ref("build", "master") }
+
+ shared_examples 'job_templates_handling' do
+ it "is correctly supported for jobs" do
+ expect(subject.size).to eq(2)
+ expect(subject.first).to eq({
+ except: nil,
+ stage: "build",
+ stage_idx: 0,
+ name: :job1,
+ only: nil,
+ commands: "execute-script-for-job",
+ tag_list: [],
+ options: {},
+ when: "on_success",
+ allow_failure: false
+ })
+ expect(subject.second).to eq({
+ except: nil,
+ stage: "build",
+ stage_idx: 0,
+ name: :job2,
+ only: nil,
+ commands: "execute-script-for-job",
+ tag_list: [],
+ options: {},
+ when: "on_success",
+ allow_failure: false
+ })
+ end
+ end
+
+ context 'when template is a job' do
+ let(:config) do
+ <<EOT
job1: &JOBTMPL
+ stage: build
script: execute-script-for-job
job2: *JOBTMPL
EOT
+ end
- config_processor = GitlabCiYamlProcessor.new(config)
+ it_behaves_like 'job_templates_handling'
+ end
- expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(2)
- expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
- except: nil,
- stage: "test",
- stage_idx: 1,
- name: :job1,
- only: nil,
- commands: "\nexecute-script-for-job",
- tag_list: [],
- options: {},
- when: "on_success",
- allow_failure: false
- })
- expect(config_processor.builds_for_stage_and_ref("test", "master").second).to eq({
- except: nil,
- stage: "test",
- stage_idx: 1,
- name: :job2,
- only: nil,
- commands: "\nexecute-script-for-job",
- tag_list: [],
- options: {},
- when: "on_success",
- allow_failure: false
- })
+ context 'when template is a hidden job' do
+ let(:config) do
+ <<EOT
+.template: &JOBTMPL
+ stage: build
+ script: execute-script-for-job
+
+job1: *JOBTMPL
+
+job2: *JOBTMPL
+EOT
+ end
+
+ it_behaves_like 'job_templates_handling'
+ end
+
+ context 'when job adds its own keys to a template definition' do
+ let(:config) do
+ <<EOT
+.template: &JOBTMPL
+ stage: build
+
+job1:
+ <<: *JOBTMPL
+ script: execute-script-for-job
+
+job2:
+ <<: *JOBTMPL
+ script: execute-script-for-job
+EOT
+ end
+
+ it_behaves_like 'job_templates_handling'
end
end
@@ -607,6 +799,27 @@ EOT
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings")
end
+ it "returns errors if job before_script parameter is not an array of strings" do
+ config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } })
+ expect do
+ GitlabCiYamlProcessor.new(config, path)
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: before_script should be an array of strings")
+ end
+
+ it "returns errors if after_script parameter is invalid" do
+ config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
+ expect do
+ GitlabCiYamlProcessor.new(config, path)
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script should be an array of strings")
+ end
+
+ it "returns errors if job after_script parameter is not an array of strings" do
+ config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } })
+ expect do
+ GitlabCiYamlProcessor.new(config, path)
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: after_script should be an array of strings")
+ end
+
it "returns errors if image parameter is invalid" do
config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
expect do
@@ -730,14 +943,14 @@ EOT
config = YAML.dump({ variables: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
end
- it "returns errors if variables is not a map of key-valued strings" do
+ it "returns errors if variables is not a map of key-value strings" do
config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
end
it "returns errors if job when is not on_success, on_failure or always" do
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index c413132abe5..1a833f255a5 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -34,9 +34,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
let(:project_identifier) { 'namespace/repo' }
let(:data) do
{
- bb_session: {
- bitbucket_access_token: "123456",
- bitbucket_access_token_secret: "secret"
+ 'bb_session' => {
+ 'bitbucket_access_token' => "123456",
+ 'bitbucket_access_token_secret' => "secret"
}
}
end
@@ -44,7 +44,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
create(
:project,
import_source: project_identifier,
- import_data: ProjectImportData.new(data: data)
+ import_data: ProjectImportData.new(credentials: data)
)
end
let(:importer) { Gitlab::BitbucketImport::Importer.new(project) }
diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
index fd05428b322..0e7ffbe9b8e 100644
--- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
@@ -2,13 +2,14 @@ require 'spec_helper'
describe Gitlab::GithubImport::IssueFormatter, lib: true do
let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
- let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') }
+ let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do
{
number: 1347,
+ milestone: nil,
state: 'open',
title: 'Found a bug',
body: "I'm having a problem with this.",
@@ -26,11 +27,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#attributes' do
context 'when issue is open' do
- let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) }
+ let(:raw_data) { double(base_data.merge(state: 'open')) }
it 'returns formatted attributes' do
expected = {
+ iid: 1347,
project: project,
+ milestone: nil,
title: 'Found a bug',
description: "*Created by: octocat*\n\nI'm having a problem with this.",
state: 'opened',
@@ -46,11 +49,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
context 'when issue is closed' do
let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
- let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) }
+ let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
it 'returns formatted attributes' do
expected = {
+ iid: 1347,
project: project,
+ milestone: nil,
title: 'Found a bug',
description: "*Created by: octocat*\n\nI'm having a problem with this.",
state: 'closed',
@@ -65,7 +70,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
context 'when it is assigned to someone' do
- let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) }
+ let(:raw_data) { double(base_data.merge(assignee: octocat)) }
it 'returns nil as assignee_id when is not a GitLab user' do
expect(issue.attributes.fetch(:assignee_id)).to be_nil
@@ -78,8 +83,23 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
end
+ context 'when it has a milestone' do
+ let(:milestone) { double(number: 45) }
+ let(:raw_data) { double(base_data.merge(milestone: milestone)) }
+
+ it 'returns nil when milestone does not exist' do
+ expect(issue.attributes.fetch(:milestone)).to be_nil
+ end
+
+ it 'returns milestone when it exists' do
+ milestone = create(:milestone, project: project, iid: 45)
+
+ expect(issue.attributes.fetch(:milestone)).to eq milestone
+ end
+ end
+
context 'when author is a GitLab user' do
- let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) }
+ let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
@@ -95,7 +115,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#has_comments?' do
context 'when number of comments is greater than zero' do
- let(:raw_data) { OpenStruct.new(base_data.merge(comments: 1)) }
+ let(:raw_data) { double(base_data.merge(comments: 1)) }
it 'returns true' do
expect(issue.has_comments?).to eq true
@@ -103,7 +123,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
context 'when number of comments is equal to zero' do
- let(:raw_data) { OpenStruct.new(base_data.merge(comments: 0)) }
+ let(:raw_data) { double(base_data.merge(comments: 0)) }
it 'returns false' do
expect(issue.has_comments?).to eq false
@@ -112,7 +132,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
describe '#number' do
- let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) }
+ let(:raw_data) { double(base_data.merge(number: 1347)) }
it 'returns pull request number' do
expect(issue.number).to eq 1347
@@ -121,7 +141,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
describe '#valid?' do
context 'when mention a pull request' do
- let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: OpenStruct.new)) }
+ let(:raw_data) { double(base_data.merge(pull_request: double)) }
it 'returns false' do
expect(issue.valid?).to eq false
@@ -129,7 +149,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
context 'when does not mention a pull request' do
- let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: nil)) }
+ let(:raw_data) { double(base_data.merge(pull_request: nil)) }
it 'returns true' do
expect(issue.valid?).to eq true
diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb
new file mode 100644
index 00000000000..e94440a7fb0
--- /dev/null
+++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::LabelFormatter, lib: true do
+
+ describe '#attributes' do
+ it 'returns formatted attributes' do
+ project = create(:project)
+ raw = double(name: 'improvements', color: 'e6e6e6')
+
+ formatter = described_class.new(project, raw)
+
+ expect(formatter.attributes).to eq({
+ project: project,
+ title: 'improvements',
+ color: '#e6e6e6'
+ })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb
new file mode 100644
index 00000000000..5a421e50581
--- /dev/null
+++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::MilestoneFormatter, lib: true do
+ let(:project) { create(:empty_project) }
+ let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
+ let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
+ let(:base_data) do
+ {
+ number: 1347,
+ state: 'open',
+ title: '1.0',
+ description: 'Version 1.0',
+ due_on: nil,
+ created_at: created_at,
+ updated_at: updated_at,
+ closed_at: nil
+ }
+ end
+
+ subject(:formatter) { described_class.new(project, raw_data)}
+
+ describe '#attributes' do
+ context 'when milestone is open' do
+ let(:raw_data) { double(base_data.merge(state: 'open')) }
+
+ it 'returns formatted attributes' do
+ expected = {
+ iid: 1347,
+ project: project,
+ title: '1.0',
+ description: 'Version 1.0',
+ state: 'active',
+ due_date: nil,
+ created_at: created_at,
+ updated_at: updated_at
+ }
+
+ expect(formatter.attributes).to eq(expected)
+ end
+ end
+
+ context 'when milestone is closed' do
+ let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
+ let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
+
+ it 'returns formatted attributes' do
+ expected = {
+ iid: 1347,
+ project: project,
+ title: '1.0',
+ description: 'Version 1.0',
+ state: 'closed',
+ due_date: nil,
+ created_at: created_at,
+ updated_at: closed_at
+ }
+
+ expect(formatter.attributes).to eq(expected)
+ end
+ end
+
+ context 'when milestone has a due date' do
+ let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') }
+ let(:raw_data) { double(base_data.merge(due_on: due_date)) }
+
+ it 'returns formatted attributes' do
+ expected = {
+ iid: 1347,
+ project: project,
+ title: '1.0',
+ description: 'Version 1.0',
+ state: 'active',
+ due_date: due_date,
+ created_at: created_at,
+ updated_at: updated_at
+ }
+
+ expect(formatter.attributes).to eq(expected)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb
index c93a3ebdaec..0f363b8b0aa 100644
--- a/spec/lib/gitlab/github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/github_import/project_creator_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do
owner: OpenStruct.new(login: "john")
)
end
- let(:namespace){ create(:group, owner: user) }
+ let(:namespace) { create(:group, owner: user) }
let(:token) { "asdffg" }
let(:access_params) { { github_access_token: token } }
@@ -27,6 +27,8 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do
project = project_creator.execute
expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git")
+ expect(project.safe_import_url).to eq("https://*****@gitlab.com/asd/vim.git")
+ expect(project.import_data.credentials).to eq(user: "asdffg", password: nil)
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index e49dcb42342..e59c0ca110e 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -2,17 +2,18 @@ require 'spec_helper'
describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:project) { create(:project) }
- let(:repository) { OpenStruct.new(id: 1, fork: false) }
+ let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository }
- let(:source_branch) { OpenStruct.new(ref: 'feature', repo: source_repo) }
+ let(:source_branch) { double(ref: 'feature', repo: source_repo) }
let(:target_repo) { repository }
- let(:target_branch) { OpenStruct.new(ref: 'master', repo: target_repo) }
- let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') }
+ let(:target_branch) { double(ref: 'master', repo: target_repo) }
+ let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:base_data) do
{
number: 1347,
+ milestone: nil,
state: 'open',
title: 'New feature',
body: 'Please pull these awesome changes',
@@ -31,10 +32,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
describe '#attributes' do
context 'when pull request is open' do
- let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) }
+ let(:raw_data) { double(base_data.merge(state: 'open')) }
it 'returns formatted attributes' do
expected = {
+ iid: 1347,
title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
@@ -42,6 +44,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project,
target_branch: 'master',
state: 'opened',
+ milestone: nil,
author_id: project.creator_id,
assignee_id: nil,
created_at: created_at,
@@ -54,10 +57,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when pull request is closed' do
let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') }
- let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) }
+ let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) }
it 'returns formatted attributes' do
expected = {
+ iid: 1347,
title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
@@ -65,6 +69,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project,
target_branch: 'master',
state: 'closed',
+ milestone: nil,
author_id: project.creator_id,
assignee_id: nil,
created_at: created_at,
@@ -77,10 +82,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when pull request is merged' do
let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') }
- let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) }
+ let(:raw_data) { double(base_data.merge(state: 'closed', merged_at: merged_at)) }
it 'returns formatted attributes' do
expected = {
+ iid: 1347,
title: 'New feature',
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
@@ -88,6 +94,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
target_project: project,
target_branch: 'master',
state: 'merged',
+ milestone: nil,
author_id: project.creator_id,
assignee_id: nil,
created_at: created_at,
@@ -99,7 +106,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'when it is assigned to someone' do
- let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) }
+ let(:raw_data) { double(base_data.merge(assignee: octocat)) }
it 'returns nil as assignee_id when is not a GitLab user' do
expect(pull_request.attributes.fetch(:assignee_id)).to be_nil
@@ -113,7 +120,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'when author is a GitLab user' do
- let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) }
+ let(:raw_data) { double(base_data.merge(user: octocat)) }
it 'returns project#creator_id as author_id when is not a GitLab user' do
expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id
@@ -125,10 +132,25 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end
end
+
+ context 'when it has a milestone' do
+ let(:milestone) { double(number: 45) }
+ let(:raw_data) { double(base_data.merge(milestone: milestone)) }
+
+ it 'returns nil when milestone does not exists' do
+ expect(pull_request.attributes.fetch(:milestone)).to be_nil
+ end
+
+ it 'returns milestone when is exists' do
+ milestone = create(:milestone, project: project, iid: 45)
+
+ expect(pull_request.attributes.fetch(:milestone)).to eq milestone
+ end
+ end
end
describe '#number' do
- let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) }
+ let(:raw_data) { double(base_data.merge(number: 1347)) }
it 'returns pull request number' do
expect(pull_request.number).to eq 1347
@@ -136,11 +158,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
describe '#valid?' do
- let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') }
+ let(:invalid_branch) { double(ref: 'invalid-branch').as_null_object }
context 'when source, and target repositories are the same' do
context 'and source and target branches exists' do
- let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) }
+ let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) }
it 'returns true' do
expect(pull_request.valid?).to eq true
@@ -148,7 +170,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'and source branch doesn not exists' do
- let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) }
+ let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) }
it 'returns false' do
expect(pull_request.valid?).to eq false
@@ -156,7 +178,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'and target branch doesn not exists' do
- let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) }
+ let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) }
it 'returns false' do
expect(pull_request.valid?).to eq false
@@ -165,8 +187,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'when source repo is a fork' do
- let(:source_repo) { OpenStruct.new(id: 2, fork: true) }
- let(:raw_data) { OpenStruct.new(base_data) }
+ let(:source_repo) { double(id: 2, fork: true) }
+ let(:raw_data) { double(base_data) }
it 'returns false' do
expect(pull_request.valid?).to eq false
@@ -174,8 +196,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'when target repo is a fork' do
- let(:target_repo) { OpenStruct.new(id: 2, fork: true) }
- let(:raw_data) { OpenStruct.new(base_data) }
+ let(:target_repo) { double(id: 2, fork: true) }
+ let(:raw_data) { double(base_data) }
it 'returns false' do
expect(pull_request.valid?).to eq false
diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
index aed2aa39e3a..1bd29b8a563 100644
--- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
@@ -2,11 +2,12 @@ require 'spec_helper'
describe Gitlab::GithubImport::WikiFormatter, lib: true do
let(:project) do
- create(:project, namespace: create(:namespace, path: 'gitlabhq'),
- import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git')
+ create(:project,
+ namespace: create(:namespace, path: 'gitlabhq'),
+ import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git')
end
- subject(:wiki) { described_class.new(project)}
+ subject(:wiki) { described_class.new(project) }
describe '#path_with_namespace' do
it 'appends .wiki to project path' do
diff --git a/spec/lib/gitlab/import_url_spec.rb b/spec/lib/gitlab/import_url_spec.rb
new file mode 100644
index 00000000000..f758cb8693c
--- /dev/null
+++ b/spec/lib/gitlab/import_url_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::ImportUrl do
+
+ let(:credentials) { { user: 'blah', password: 'password' } }
+ let(:import_url) do
+ Gitlab::ImportUrl.new("https://github.com/me/project.git", credentials: credentials)
+ end
+
+ describe :full_url do
+ it { expect(import_url.full_url).to eq("https://blah:password@github.com/me/project.git") }
+ end
+
+ describe :sanitized_url do
+ it { expect(import_url.sanitized_url).to eq("https://github.com/me/project.git") }
+ end
+
+ describe :credentials do
+ it { expect(import_url.credentials).to eq(credentials) }
+ end
+end
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index ad4290c43bb..5c885a7a982 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -33,8 +33,16 @@ describe Gitlab::Metrics::Instrumentation do
described_class.instrument_method(@dummy, :foo)
end
- it 'renames the original method' do
- expect(@dummy).to respond_to(:_original_foo)
+ it 'instruments the Class' do
+ target = @dummy.singleton_class
+
+ expect(described_class.instrumented?(target)).to eq(true)
+ end
+
+ it 'defines a proxy method' do
+ mod = described_class.proxy_module(@dummy.singleton_class)
+
+ expect(mod.method_defined?(:foo)).to eq(true)
end
it 'calls the instrumented method with the correct arguments' do
@@ -76,6 +84,14 @@ describe Gitlab::Metrics::Instrumentation do
expect(dummy.method(:test).arity).to eq(0)
end
+
+ describe 'when a module is instrumented multiple times' do
+ it 'calls the instrumented method with the correct arguments' do
+ described_class.instrument_method(@dummy, :foo)
+
+ expect(@dummy.foo).to eq('foo')
+ end
+ end
end
describe 'with metrics disabled' do
@@ -86,7 +102,9 @@ describe Gitlab::Metrics::Instrumentation do
it 'does not instrument the method' do
described_class.instrument_method(@dummy, :foo)
- expect(@dummy).to_not respond_to(:_original_foo)
+ target = @dummy.singleton_class
+
+ expect(described_class.instrumented?(target)).to eq(false)
end
end
end
@@ -100,8 +118,14 @@ describe Gitlab::Metrics::Instrumentation do
instrument_instance_method(@dummy, :bar)
end
- it 'renames the original method' do
- expect(@dummy.method_defined?(:_original_bar)).to eq(true)
+ it 'instruments instances of the Class' do
+ expect(described_class.instrumented?(@dummy)).to eq(true)
+ end
+
+ it 'defines a proxy method' do
+ mod = described_class.proxy_module(@dummy)
+
+ expect(mod.method_defined?(:bar)).to eq(true)
end
it 'calls the instrumented method with the correct arguments' do
@@ -144,7 +168,7 @@ describe Gitlab::Metrics::Instrumentation do
described_class.
instrument_instance_method(@dummy, :bar)
- expect(@dummy.method_defined?(:_original_bar)).to eq(false)
+ expect(described_class.instrumented?(@dummy)).to eq(false)
end
end
end
@@ -167,18 +191,17 @@ describe Gitlab::Metrics::Instrumentation do
it 'recursively instruments a class hierarchy' do
described_class.instrument_class_hierarchy(@dummy)
- expect(@child1).to respond_to(:_original_child1_foo)
- expect(@child2).to respond_to(:_original_child2_foo)
+ expect(described_class.instrumented?(@child1.singleton_class)).to eq(true)
+ expect(described_class.instrumented?(@child2.singleton_class)).to eq(true)
- expect(@child1.method_defined?(:_original_child1_bar)).to eq(true)
- expect(@child2.method_defined?(:_original_child2_bar)).to eq(true)
+ expect(described_class.instrumented?(@child1)).to eq(true)
+ expect(described_class.instrumented?(@child2)).to eq(true)
end
it 'does not instrument the root module' do
described_class.instrument_class_hierarchy(@dummy)
- expect(@dummy).to_not respond_to(:_original_foo)
- expect(@dummy.method_defined?(:_original_bar)).to eq(false)
+ expect(described_class.instrumented?(@dummy)).to eq(false)
end
end
@@ -190,7 +213,7 @@ describe Gitlab::Metrics::Instrumentation do
it 'instruments all public class methods' do
described_class.instrument_methods(@dummy)
- expect(@dummy).to respond_to(:_original_foo)
+ expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true)
end
it 'only instruments methods directly defined in the module' do
@@ -223,7 +246,7 @@ describe Gitlab::Metrics::Instrumentation do
it 'instruments all public instance methods' do
described_class.instrument_instance_methods(@dummy)
- expect(@dummy.method_defined?(:_original_bar)).to eq(true)
+ expect(described_class.instrumented?(@dummy)).to eq(true)
end
it 'only instruments methods directly defined in the module' do
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 7bc070a4d09..e3293a01207 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -28,6 +28,9 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
expect(transaction).to receive(:increment).
with(:sql_duration, 0.2)
+ expect(transaction).to receive(:increment).
+ with(:sql_count, 1)
+
subscriber.sql(event)
end
end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 10177c0e8dd..96f7eabbca6 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -123,4 +123,28 @@ describe Gitlab::Metrics do
end
end
end
+
+ describe '.action=' do
+ context 'without a transaction' do
+ it 'does nothing' do
+ expect_any_instance_of(Gitlab::Metrics::Transaction).
+ not_to receive(:action=)
+
+ Gitlab::Metrics.action = 'foo'
+ end
+ end
+
+ context 'with a transaction' do
+ it 'sets the action of a transaction' do
+ trans = Gitlab::Metrics::Transaction.new
+
+ expect(Gitlab::Metrics).to receive(:current_transaction).
+ and_return(trans)
+
+ expect(trans).to receive(:action=).with('foo')
+
+ Gitlab::Metrics.action = 'foo'
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
index da652677443..f093d0a0d8b 100644
--- a/spec/lib/gitlab/note_data_builder_spec.rb
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -4,13 +4,12 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:data) { Gitlab::NoteDataBuilder.build(note, user) }
- let(:note_url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
before(:each) do
expect(data).to have_key(:object_attributes)
expect(data[:object_attributes]).to have_key(:url)
- expect(data[:object_attributes][:url]).to eq(note_url)
+ expect(data[:object_attributes][:url]).to eq(Gitlab::UrlBuilder.build(note))
expect(data[:object_kind]).to eq('note')
expect(data[:user]).to eq(user.hook_attrs)
end
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
index 961022b9d12..7fc34139eff 100644
--- a/spec/lib/gitlab/push_data_builder_spec.rb
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -14,11 +14,11 @@ describe Gitlab::PushDataBuilder, lib: true do
it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) }
it { expect(data[:total_commits_count]).to eq(3) }
- it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) }
- it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) }
+ it { expect(data[:commits].first[:added]).to eq(['gitlab-grack']) }
+ it { expect(data[:commits].first[:modified]).to eq(['.gitmodules']) }
it { expect(data[:commits].first[:removed]).to eq([]) }
- include_examples 'project hook data'
+ include_examples 'project hook data with deprecateds'
include_examples 'deprecated repository hook data'
end
@@ -34,9 +34,18 @@ describe Gitlab::PushDataBuilder, lib: true do
it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') }
it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
+ it { expect(data[:user_id]).to eq(user.id) }
+ it { expect(data[:user_name]).to eq(user.name) }
+ it { expect(data[:user_email]).to eq(user.email) }
+ it { expect(data[:user_avatar]).to eq(user.avatar_url) }
+ it { expect(data[:project_id]).to eq(project.id) }
+ it { expect(data[:project]).to be_a(Hash) }
it { expect(data[:commits]).to be_empty }
it { expect(data[:total_commits_count]).to be_zero }
+ include_examples 'project hook data with deprecateds'
+ include_examples 'deprecated repository hook data'
+
it 'does not raise an error when given nil commits' do
expect { described_class.build(spy, spy, spy, spy, spy, nil) }.
not_to raise_error
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index f023be6ae45..bf11472407a 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -1,77 +1,119 @@
require 'spec_helper'
describe Gitlab::UrlBuilder, lib: true do
- describe 'When asking for an issue' do
- it 'returns the issue url' do
- issue = create(:issue)
- url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
- expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
- end
- end
+ describe '.build' do
+ context 'when passing a Commit' do
+ it 'returns a proper URL' do
+ commit = build_stubbed(:commit)
- describe 'When asking for an merge request' do
- it 'returns the merge request url' do
- merge_request = create(:merge_request)
- url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
+ url = described_class.build(commit)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}"
+ end
end
- end
- describe 'When asking for a note on commit' do
- let(:note) { create(:note_on_commit) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ context 'when passing an Issue' do
+ it 'returns a proper URL' do
+ issue = build_stubbed(:issue, iid: 42)
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ url = described_class.build(issue)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
+ end
end
- end
- describe 'When asking for a note on commit diff' do
- let(:note) { create(:note_on_commit_diff) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ context 'when passing a MergeRequest' do
+ it 'returns a proper URL' do
+ merge_request = build_stubbed(:merge_request, iid: 42)
+
+ url = described_class.build(merge_request)
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
+ end
end
- end
- describe 'When asking for a note on issue' do
- let(:issue) { create(:issue) }
- let(:note) { create(:note_on_issue, noteable_id: issue.id) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ context 'when passing a Note' do
+ context 'on a Commit' do
+ it 'returns a proper URL' do
+ note = build_stubbed(:note_on_commit)
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
- end
- end
+ url = described_class.build(note)
- describe 'When asking for a note on merge request' do
- let(:merge_request) { create(:merge_request) }
- let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
- end
- end
+ context 'on a CommitDiff' do
+ it 'returns a proper URL' do
+ note = build_stubbed(:note_on_commit_diff)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
- describe 'When asking for a note on merge request diff' do
- let(:merge_request) { create(:merge_request) }
- let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ context 'on an Issue' do
+ it 'returns a proper URL' do
+ issue = create(:issue, iid: 42)
+ note = build_stubbed(:note_on_issue, noteable: issue)
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a MergeRequest' do
+ it 'returns a proper URL' do
+ merge_request = create(:merge_request, iid: 42)
+ note = build_stubbed(:note_on_merge_request, noteable: merge_request)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a MergeRequestDiff' do
+ it 'returns a proper URL' do
+ merge_request = create(:merge_request, iid: 42)
+ note = build_stubbed(:note_on_merge_request_diff, noteable: merge_request)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a ProjectSnippet' do
+ it 'returns a proper URL' do
+ project_snippet = create(:project_snippet)
+ note = build_stubbed(:note_on_project_snippet, noteable: project_snippet)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
+ end
+ end
+
+ context 'on another object' do
+ it 'returns a proper URL' do
+ project = build_stubbed(:project)
+
+ expect { described_class.build(project) }.
+ to raise_error(NotImplementedError, 'No URL builder defined for Project')
+ end
+ end
end
- end
- describe 'When asking for a note on project snippet' do
- let(:snippet) { create(:project_snippet) }
- let(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
- let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ context 'when passing a WikiPage' do
+ it 'returns a proper URL' do
+ wiki_page = build(:wiki_page)
+ url = described_class.build(wiki_page)
- it 'returns the note url' do
- expect(url).to eq "#{Settings.gitlab['url']}/#{snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
+ expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}"
+ end
end
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 631b5094f42..495c5cbac00 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -213,7 +213,7 @@ describe Notify do
it_behaves_like 'an unsubscribeable thread'
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
end
it 'contains a link to the new merge request' do
@@ -268,7 +268,7 @@ describe Notify do
end
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
end
it 'contains the name of the previous assignee' do
@@ -302,7 +302,7 @@ describe Notify do
end
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
end
it 'contains the names of the added labels' do
@@ -331,7 +331,7 @@ describe Notify do
end
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/i
end
it 'contains the new status' do
@@ -364,7 +364,7 @@ describe Notify do
end
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
end
it 'contains the new status' do
@@ -502,7 +502,7 @@ describe Notify do
it_behaves_like 'an unsubscribeable thread'
it 'has the correct subject' do
- is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
+ is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
end
it 'contains a link to the merge request note' do
diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb
new file mode 100644
index 00000000000..00613c7b671
--- /dev/null
+++ b/spec/mailers/repository_check_mailer_spec.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+
+describe RepositoryCheckMailer do
+ include EmailSpec::Matchers
+
+ describe '.notify' do
+ it 'emails all admins' do
+ admins = create_list(:admin, 3)
+
+ mail = described_class.notify(1)
+
+ expect(mail).to deliver_to admins.map(&:email)
+ end
+
+ it 'mentions the number of failed checks' do
+ mail = described_class.notify(3)
+
+ expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check'
+ end
+ end
+end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index b7457808040..b5d356aa066 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -238,6 +238,22 @@ describe Ci::Build, models: true do
it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
end
+
+ context 'when job variables are defined' do
+ ##
+ # Job-level variables are defined in gitlab_ci.yml fixture
+ #
+ context 'when job variables are unique' do
+ let(:build) { create(:ci_build, name: 'staging') }
+
+ it 'includes job variables' do
+ expect(subject).to include(
+ { key: :KEY1, value: 'value1', public: true },
+ { key: :KEY2, value: 'value2', public: true }
+ )
+ end
+ end
+ end
end
end
end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index fb3b61ad7c7..82c18aaa01a 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -27,6 +27,8 @@ describe Ci::Commit, models: true do
it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha }
+ it { is_expected.to validate_presence_of :status }
+ it { is_expected.to delegate_method(:stages).to(:statuses) }
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
@@ -297,4 +299,98 @@ describe Ci::Commit, models: true do
expect(commit.coverage).to be_nil
end
end
+
+ describe '#retryable?' do
+ subject { commit.retryable? }
+
+ context 'no failed builds' do
+ before do
+ FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'success'
+ end
+
+ it 'be not retryable' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'with failed builds' do
+ before do
+ FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'running'
+ FactoryGirl.create :ci_build, name: "rubocop", commit: commit, status: 'failed'
+ end
+
+ it 'be retryable' do
+ is_expected.to be_truthy
+ end
+ end
+ end
+
+ describe '#stages' do
+ let(:commit2) { FactoryGirl.create :ci_commit, project: project }
+ subject { CommitStatus.where(commit: [commit, commit2]).stages }
+
+ before do
+ FactoryGirl.create :ci_build, commit: commit2, stage: 'test', stage_idx: 1
+ FactoryGirl.create :ci_build, commit: commit, stage: 'build', stage_idx: 0
+ end
+
+ it 'return all stages' do
+ is_expected.to eq(%w(build test))
+ end
+ end
+
+ describe '#update_state' do
+ it 'execute update_state after touching object' do
+ expect(commit).to receive(:update_state).and_return(true)
+ commit.touch
+ end
+
+ context 'dependent objects' do
+ let(:commit_status) { build :commit_status, commit: commit }
+
+ it 'execute update_state after saving dependent object' do
+ expect(commit).to receive(:update_state).and_return(true)
+ commit_status.save
+ end
+ end
+
+ context 'update state' do
+ let(:current) { Time.now.change(usec: 0) }
+ let(:build) { FactoryGirl.create :ci_build, :success, commit: commit, started_at: current - 120, finished_at: current - 60 }
+
+ before do
+ build
+ end
+
+ [:status, :started_at, :finished_at, :duration].each do |param|
+ it "update #{param}" do
+ expect(commit.send(param)).to eq(build.send(param))
+ end
+ end
+ end
+ end
+
+ describe '#branch?' do
+ subject { commit.branch? }
+
+ context 'is not a tag' do
+ before do
+ commit.tag = false
+ end
+
+ it 'return true when tag is set to false' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'is not a tag' do
+ before do
+ commit.tag = true
+ end
+
+ it 'return false when tag is set to true' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 0e9111c8029..ad47e338a33 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -163,4 +163,12 @@ eos
it { expect(commit.reverts_commit?(another_commit)).to be_truthy }
end
end
+
+ describe '#ci_commits' do
+ # TODO: kamil
+ end
+
+ describe '#status' do
+ # TODO: kamil
+ end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 1c64947f1f5..971e6750375 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -182,4 +182,54 @@ describe CommitStatus, models: true do
is_expected.to eq([@commit1, @commit2])
end
end
+
+ describe '#before_sha' do
+ subject { commit_status.before_sha }
+
+ context 'when no before_sha is set for ci::commit' do
+ before { commit.before_sha = nil }
+
+ it 'return blank sha' do
+ is_expected.to eq(Gitlab::Git::BLANK_SHA)
+ end
+ end
+
+ context 'for before_sha set for ci::commit' do
+ let(:value) { '1234' }
+ before { commit.before_sha = value }
+
+ it 'return the set value' do
+ is_expected.to eq(value)
+ end
+ end
+ end
+
+ describe '#stages' do
+ before do
+ FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'success'
+ FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'failed'
+ FactoryGirl.create :commit_status, commit: commit, stage: 'deploy', stage_idx: 2, status: 'running'
+ FactoryGirl.create :commit_status, commit: commit, stage: 'test', stage_idx: 1, status: 'success'
+ end
+
+ context 'stages list' do
+ subject { CommitStatus.where(commit: commit).stages }
+
+ it 'return ordered list of stages' do
+ is_expected.to eq(%w(build test deploy))
+ end
+ end
+
+ context 'stages with statuses' do
+ subject { CommitStatus.where(commit: commit).stages_status }
+
+ it 'return list of stages with statuses' do
+ is_expected.to eq({
+ 'build' => 'failed',
+ 'test' => 'success',
+ 'deploy' => 'running'
+ })
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index b16ccc6e305..4a4cd093435 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -212,4 +212,34 @@ describe Issue, "Issuable" do
expect(issue.downvotes).to eq(1)
end
end
+
+ describe ".with_label" do
+ let(:project) { create(:project, :public) }
+ let(:bug) { create(:label, project: project, title: 'bug') }
+ let(:feature) { create(:label, project: project, title: 'feature') }
+ let(:enhancement) { create(:label, project: project, title: 'enhancement') }
+ let(:issue1) { create(:issue, title: "Bugfix1", project: project) }
+ let(:issue2) { create(:issue, title: "Bugfix2", project: project) }
+ let(:issue3) { create(:issue, title: "Feature1", project: project) }
+
+ before(:each) do
+ issue1.labels << bug
+ issue1.labels << feature
+ issue2.labels << bug
+ issue2.labels << enhancement
+ issue3.labels << feature
+ end
+
+ it 'finds the correct issue containing just enhancement label' do
+ expect(Issue.with_label(enhancement.title)).to match_array([issue2])
+ end
+
+ it 'finds the correct issues containing the same label' do
+ expect(Issue.with_label(bug.title)).to match_array([issue1, issue2])
+ end
+
+ it 'finds the correct issues containing only both labels' do
+ expect(Issue.with_label([bug.title, enhancement.title])).to match_array([issue2])
+ end
+ end
end
diff --git a/spec/lib/ci/status_spec.rb b/spec/models/concerns/statuseable_spec.rb
index 886b82a7afa..dacbd3034c0 100644
--- a/spec/lib/ci/status_spec.rb
+++ b/spec/models/concerns/statuseable_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe CiStatus do
+describe Statuseable do
before do
@object = Object.new
- @object.extend(CiStatus::ClassMethods)
+ @object.extend(Statuseable::ClassMethods)
end
describe '.status' do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 89909c2bcd7..0c3cd13f399 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -30,32 +30,29 @@ describe Event, models: true do
it { is_expected.to respond_to(:commits) }
end
+ describe 'Callbacks' do
+ describe 'after_create :reset_project_activity' do
+ let(:project) { create(:project) }
+
+ context "project's last activity was less than 5 minutes ago" do
+ it 'does not update project.last_activity_at if it has been touched less than 5 minutes ago' do
+ create_event(project, project.owner)
+ project.update_column(:last_activity_at, 5.minutes.ago)
+ project_last_activity_at = project.last_activity_at
+
+ create_event(project, project.owner)
+
+ expect(project.last_activity_at).to eq(project_last_activity_at)
+ end
+ end
+ end
+ end
+
describe "Push event" do
before do
project = create(:project)
@user = project.owner
-
- data = {
- before: Gitlab::Git::BLANK_SHA,
- after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
- ref: "refs/heads/master",
- user_id: @user.id,
- user_name: @user.name,
- repository: {
- name: project.name,
- url: "localhost/rubinius",
- description: "",
- homepage: "localhost/rubinius",
- private: true
- }
- }
-
- @event = Event.create(
- project: project,
- action: Event::PUSHED,
- data: data,
- author_id: @user.id
- )
+ @event = create_event(project, @user)
end
it { expect(@event.push?).to be_truthy }
@@ -143,4 +140,28 @@ describe Event, models: true do
it { is_expected.to eq([event2]) }
end
end
+
+ def create_event(project, user, attrs = {})
+ data = {
+ before: Gitlab::Git::BLANK_SHA,
+ after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
+ ref: "refs/heads/master",
+ user_id: user.id,
+ user_name: user.name,
+ repository: {
+ name: project.name,
+ url: "localhost/rubinius",
+ description: "",
+ homepage: "localhost/rubinius",
+ private: true
+ }
+ }
+
+ Event.create({
+ project: project,
+ action: Event::PUSHED,
+ data: data,
+ author_id: user.id
+ }.merge(attrs))
+ end
end
diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb
index 9b144dd1ecc..4fc3b065592 100644
--- a/spec/models/external_issue_spec.rb
+++ b/spec/models/external_issue_spec.rb
@@ -36,4 +36,19 @@ describe ExternalIssue, models: true do
expect(issue.title).to eq "External Issue #{issue}"
end
end
+
+ describe '#reference_link_text' do
+ context 'if issue id has a prefix' do
+ it 'returns the issue ID' do
+ expect(issue.reference_link_text).to eq 'EXT-1234'
+ end
+ end
+
+ context 'if issue id is a number' do
+ let(:issue) { described_class.new('1234', project) }
+ it 'returns the issue ID prefixed by #' do
+ expect(issue.reference_link_text).to eq '#1234'
+ end
+ end
+ end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 15052aaca28..060e6599104 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -191,11 +191,36 @@ describe Issue, models: true do
end
describe '#related_branches' do
- it "selects the right branches" do
+ let(:user) { build(:admin) }
+
+ before do
allow(subject.project.repository).to receive(:branch_names).
- and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
+ and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
+
+ # Without this stub, the `create(:merge_request)` above fails because it can't find
+ # the source branch. This seems like a reasonable compromise, in comparison with
+ # setting up a full repo here.
+ allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff)
+ end
- expect(subject.related_branches).to eq([subject.to_branch_name])
+ it "selects the right branches when there are no referenced merge requests" do
+ expect(subject.related_branches(user)).to eq([subject.to_branch_name, "#{subject.iid}-branch"])
+ end
+
+ it "selects the right branches when there is a referenced merge request" do
+ merge_request = create(:merge_request, { description: "Closes ##{subject.iid}",
+ source_project: subject.project,
+ source_branch: "#{subject.iid}-branch" })
+ merge_request.create_cross_references!(user)
+ expect(subject.referenced_merge_requests).to_not be_empty
+ expect(subject.related_branches(user)).to eq([subject.to_branch_name])
+ end
+
+ it 'excludes stable branches from the related branches' do
+ allow(subject.project.repository).to receive(:branch_names).
+ and_return(["#{subject.iid}-0-stable"])
+
+ expect(subject.related_branches(user)).to eq []
end
end
@@ -211,10 +236,19 @@ describe Issue, models: true do
end
describe "#to_branch_name" do
- let(:issue) { create(:issue, title: 'a' * 30) }
+ let(:issue) { create(:issue, title: 'testing-issue') }
+
+ it 'starts with the issue iid' do
+ expect(issue.to_branch_name).to match /\A#{issue.iid}-[A-Za-z\-]+\z/
+ end
+
+ it "contains the issue title if not confidential" do
+ expect(issue.to_branch_name).to match /testing-issue\z/
+ end
- it "starts with the issue iid" do
- expect(issue.to_branch_name).to match /-#{issue.iid}\z/
+ it "does not contain the issue title if confidential" do
+ issue = create(:issue, title: 'testing-issue', confidential: true)
+ expect(issue.to_branch_name).to match /confidential-issue\z/
end
end
end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index c34b2487ecf..31b2c90122d 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -21,74 +21,232 @@
require 'spec_helper'
describe BambooService, models: true do
- describe "Associations" do
+ describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- context "when a password was previously set" do
- before do
- @bamboo_service = BambooService.create(
- project: create(:project),
- properties: {
- bamboo_url: 'http://gitlab.com',
- username: 'mic',
- password: "password"
- }
- )
+ describe 'Validations' do
+ describe '#bamboo_url' do
+ it 'does not validate the presence of bamboo_url if service is not active' do
+ bamboo_service = service
+ bamboo_service.active = false
+
+ expect(bamboo_service).not_to validate_presence_of(:bamboo_url)
+ end
+
+ it 'validates the presence of bamboo_url if service is active' do
+ bamboo_service = service
+ bamboo_service.active = true
+
+ expect(bamboo_service).to validate_presence_of(:bamboo_url)
+ end
+ end
+
+ describe '#build_key' do
+ it 'does not validate the presence of build_key if service is not active' do
+ bamboo_service = service
+ bamboo_service.active = false
+
+ expect(bamboo_service).not_to validate_presence_of(:build_key)
end
-
- it "reset password if url changed" do
- @bamboo_service.bamboo_url = 'http://gitlab1.com'
- @bamboo_service.save
- expect(@bamboo_service.password).to be_nil
+
+ it 'validates the presence of build_key if service is active' do
+ bamboo_service = service
+ bamboo_service.active = true
+
+ expect(bamboo_service).to validate_presence_of(:build_key)
+ end
+ end
+
+ describe '#username' do
+ it 'does not validate the presence of username if service is not active' do
+ bamboo_service = service
+ bamboo_service.active = false
+
+ expect(bamboo_service).not_to validate_presence_of(:username)
+ end
+
+ it 'does not validate the presence of username if username is nil' do
+ bamboo_service = service
+ bamboo_service.active = true
+ bamboo_service.password = nil
+
+ expect(bamboo_service).not_to validate_presence_of(:username)
+ end
+
+ it 'validates the presence of username if service is active and username is present' do
+ bamboo_service = service
+ bamboo_service.active = true
+ bamboo_service.password = 'secret'
+
+ expect(bamboo_service).to validate_presence_of(:username)
end
-
- it "does not reset password if username changed" do
- @bamboo_service.username = "some_name"
- @bamboo_service.save
- expect(@bamboo_service.password).to eq("password")
+ end
+
+ describe '#password' do
+ it 'does not validate the presence of password if service is not active' do
+ bamboo_service = service
+ bamboo_service.active = false
+
+ expect(bamboo_service).not_to validate_presence_of(:password)
end
- it "does not reset password if new url is set together with password, even if it's the same password" do
- @bamboo_service.bamboo_url = 'http://gitlab_edited.com'
- @bamboo_service.password = 'password'
- @bamboo_service.save
- expect(@bamboo_service.password).to eq("password")
- expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com")
+ it 'does not validate the presence of password if username is nil' do
+ bamboo_service = service
+ bamboo_service.active = true
+ bamboo_service.username = nil
+
+ expect(bamboo_service).not_to validate_presence_of(:password)
end
- it "should reset password if url changed, even if setter called multiple times" do
- @bamboo_service.bamboo_url = 'http://gitlab1.com'
- @bamboo_service.bamboo_url = 'http://gitlab1.com'
- @bamboo_service.save
- expect(@bamboo_service.password).to be_nil
+ it 'validates the presence of password if service is active and username is present' do
+ bamboo_service = service
+ bamboo_service.active = true
+ bamboo_service.username = 'john'
+
+ expect(bamboo_service).to validate_presence_of(:password)
end
end
-
- context "when no password was previously set" do
- before do
- @bamboo_service = BambooService.create(
- project: create(:project),
- properties: {
- bamboo_url: 'http://gitlab.com',
- username: 'mic'
- }
- )
+ end
+
+ describe 'Callbacks' do
+ describe 'before_update :reset_password' do
+ context 'when a password was previously set' do
+ it 'resets password if url changed' do
+ bamboo_service = service
+
+ bamboo_service.bamboo_url = 'http://gitlab1.com'
+ bamboo_service.save
+
+ expect(bamboo_service.password).to be_nil
+ end
+
+ it 'does not reset password if username changed' do
+ bamboo_service = service
+
+ bamboo_service.username = 'some_name'
+ bamboo_service.save
+
+ expect(bamboo_service.password).to eq('password')
+ end
+
+ it "does not reset password if new url is set together with password, even if it's the same password" do
+ bamboo_service = service
+
+ bamboo_service.bamboo_url = 'http://gitlab_edited.com'
+ bamboo_service.password = 'password'
+ bamboo_service.save
+
+ expect(bamboo_service.password).to eq('password')
+ expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com')
+ end
end
- it "saves password if new url is set together with password" do
- @bamboo_service.bamboo_url = 'http://gitlab_edited.com'
- @bamboo_service.password = 'password'
- @bamboo_service.save
- expect(@bamboo_service.password).to eq("password")
- expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com")
+ it 'saves password if new url is set together with password when no password was previously set' do
+ bamboo_service = service
+ bamboo_service.password = nil
+
+ bamboo_service.bamboo_url = 'http://gitlab_edited.com'
+ bamboo_service.password = 'password'
+ bamboo_service.save
+
+ expect(bamboo_service.password).to eq('password')
+ expect(bamboo_service.bamboo_url).to eq('http://gitlab_edited.com')
end
+ end
+ end
+
+ describe '#build_page' do
+ it 'returns a specific URL when status is 500' do
+ stub_request(status: 500)
+
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+ end
+
+ it 'returns a specific URL when response has no results' do
+ stub_request(body: %Q({"results":{"results":{"size":"0"}}}))
+
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+ end
+
+ it 'returns a build URL when bamboo_url has no trailing slash' do
+ stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
+
+ expect(service(bamboo_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+ end
+
+ it 'returns a build URL when bamboo_url has a trailing slash' do
+ stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
+
+ expect(service(bamboo_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+ end
+ end
+
+ describe '#commit_status' do
+ it 'sets commit status to :error when status is 500' do
+ stub_request(status: 500)
+
+ expect(service.commit_status('123', 'unused')).to eq(:error)
+ end
+
+ it 'sets commit status to "pending" when status is 404' do
+ stub_request(status: 404)
+
+ expect(service.commit_status('123', 'unused')).to eq('pending')
+ end
+
+ it 'sets commit status to "pending" when response has no results' do
+ stub_request(body: %Q({"results":{"results":{"size":"0"}}}))
+
+ expect(service.commit_status('123', 'unused')).to eq('pending')
+ end
+
+ it 'sets commit status to "success" when build state contains Success' do
+ stub_request(build_state: 'YAY Success!')
+ expect(service.commit_status('123', 'unused')).to eq('success')
end
+
+ it 'sets commit status to "failed" when build state contains Failed' do
+ stub_request(build_state: 'NO Failed!')
+
+ expect(service.commit_status('123', 'unused')).to eq('failed')
+ end
+
+ it 'sets commit status to "pending" when build state contains Pending' do
+ stub_request(build_state: 'NO Pending!')
+
+ expect(service.commit_status('123', 'unused')).to eq('pending')
+ end
+
+ it 'sets commit status to :error when build state is unknown' do
+ stub_request(build_state: 'FOO BAR!')
+
+ expect(service.commit_status('123', 'unused')).to eq(:error)
+ end
+ end
+
+ def service(bamboo_url: 'http://gitlab.com')
+ described_class.create(
+ project: build_stubbed(:empty_project),
+ properties: {
+ bamboo_url: bamboo_url,
+ username: 'mic',
+ password: 'password',
+ build_key: 'foo'
+ }
+ )
+ end
+
+ def stub_request(status: 200, body: nil, build_state: 'success')
+ bamboo_full_url = 'http://mic:password@gitlab.com/rest/api/latest/result?label=123&os_authType=basic'
+ body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}})
+
+ WebMock.stub_request(:get, bamboo_full_url).to_return(
+ status: status,
+ headers: { 'Content-Type' => 'application/json' },
+ body: body
+ )
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 2ccbff553f0..7c23c2efccd 100644
--- a/spec/models/project_services/builds_email_service_spec.rb
+++ b/spec/models/project_services/builds_email_service_spec.rb
@@ -3,9 +3,10 @@ require 'spec_helper'
describe BuildsEmailService do
let(:build) { create(:ci_build) }
let(:data) { Gitlab::BuildDataBuilder.build(build) }
- let(:service) { BuildsEmailService.new }
+ let!(:project) { create(:project, :public, ci_id: 1) }
+ let(:service) { described_class.new(project: project, active: true) }
- describe :execute do
+ describe '#execute' do
it 'sends email' do
service.recipients = 'test@gitlab.com'
data[:build_status] = 'failed'
@@ -40,4 +41,36 @@ describe BuildsEmailService do
service.execute(data)
end
end
+
+ describe 'validations' do
+
+ context 'when pusher is not added' do
+ before { service.add_pusher = false }
+
+ it 'does not allow empty recipient input' do
+ service.recipients = ''
+ expect(service.valid?).to be false
+ end
+
+ it 'does allow non-empty recipient input' do
+ service.recipients = 'test@example.com'
+ expect(service.valid?).to be true
+ end
+
+ end
+
+ context 'when pusher is added' do
+ before { service.add_pusher = true }
+
+ it 'does allow empty recipient input' do
+ service.recipients = ''
+ expect(service.valid?).to be true
+ end
+
+ it 'does allow non-empty recipient input' do
+ service.recipients = 'test@example.com'
+ expect(service.valid?).to be true
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 91dd92b7c67..d878162a220 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -152,7 +152,7 @@ describe HipchatService, models: true do
obj_attr = merge_sample_data[:object_attributes]
expect(message).to eq("#{user.name} opened " \
- "<a href=\"#{obj_attr[:url]}\">merge request ##{obj_attr["iid"]}</a> in " \
+ "<a href=\"#{obj_attr[:url]}\">merge request !#{obj_attr["iid"]}</a> in " \
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
"<b>Awesome merge request</b>" \
"<pre>please fix</pre>")
@@ -202,7 +202,7 @@ describe HipchatService, models: true do
title = data[:merge_request]['title']
expect(message).to eq("#{user.name} commented on " \
- "<a href=\"#{obj_attr[:url]}\">merge request ##{merge_id}</a> in " \
+ "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
"<b>#{title}</b>" \
"<pre>merge request note</pre>")
diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb
index dae8bd90922..224c7ceabe8 100644
--- a/spec/models/project_services/slack_service/merge_message_spec.rb
+++ b/spec/models/project_services/slack_service/merge_message_spec.rb
@@ -31,7 +31,7 @@ describe SlackService::MergeMessage, models: true do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'Test User opened <somewhere.com/merge_requests/100|merge request #100> '\
+ 'Test User opened <somewhere.com/merge_requests/100|merge request !100> '\
'in <somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
@@ -43,7 +43,7 @@ describe SlackService::MergeMessage, models: true do
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'Test User closed <somewhere.com/merge_requests/100|merge request #100> '\
+ 'Test User closed <somewhere.com/merge_requests/100|merge request !100> '\
'in <somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
index 06006b9a4f5..d37590cab75 100644
--- a/spec/models/project_services/slack_service/note_message_spec.rb
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -63,7 +63,7 @@ describe SlackService::NoteMessage, models: true do
it 'returns a message regarding notes on a merge request' do
message = SlackService::NoteMessage.new(@args)
expect(message.pretext).to eq("Test User commented on " \
- "<url|merge request #30> in <somewhere.com|project_name>: " \
+ "<url|merge request !30> in <somewhere.com|project_name>: " \
"*merge request title*")
expected_attachments = [
{
diff --git a/spec/models/project_services/slack_service/wiki_page_message_spec.rb b/spec/models/project_services/slack_service/wiki_page_message_spec.rb
new file mode 100644
index 00000000000..6ecab645b49
--- /dev/null
+++ b/spec/models/project_services/slack_service/wiki_page_message_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+describe SlackService::WikiPageMessage, models: true do
+ subject { described_class.new(args) }
+
+ let(:args) do
+ {
+ user: {
+ name: 'Test User',
+ username: 'Test User'
+ },
+ project_name: 'project_name',
+ project_url: 'somewhere.com',
+ object_attributes: {
+ title: 'Wiki page title',
+ url: 'url',
+ content: 'Wiki page description'
+ }
+ }
+ end
+
+ describe '#pretext' do
+ context 'when :action == "create"' do
+ before { args[:object_attributes][:action] = 'create' }
+
+ it 'returns a message that a new wiki page was created' do
+ expect(subject.pretext).to eq(
+ 'Test User created <url|wiki page> in <somewhere.com|project_name>: '\
+ '*Wiki page title*')
+ end
+ end
+
+ context 'when :action == "update"' do
+ before { args[:object_attributes][:action] = 'update' }
+
+ it 'returns a message that a wiki page was updated' do
+ expect(subject.pretext).to eq(
+ 'Test User edited <url|wiki page> in <somewhere.com|project_name>: '\
+ '*Wiki page title*')
+ end
+ end
+ end
+
+ describe '#attachments' do
+ let(:color) { '#345' }
+
+ context 'when :action == "create"' do
+ before { args[:object_attributes][:action] = 'create' }
+
+
+ it 'it returns the attachment for a new wiki page' do
+ expect(subject.attachments).to eq([
+ {
+ text: "Wiki page description",
+ color: color,
+ }
+ ])
+ end
+ end
+
+ context 'when :action == "update"' do
+ before { args[:object_attributes][:action] = 'update' }
+
+ it 'it returns the attachment for an updated wiki page' do
+ expect(subject.attachments).to eq([
+ {
+ text: "Wiki page description",
+ color: color,
+ }
+ ])
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index a9e0afad90f..478d59be08b 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -75,6 +75,17 @@ describe SlackService, models: true do
@merge_request = merge_service.execute
@merge_sample_data = merge_service.hook_data(@merge_request,
'open')
+
+ opts = {
+ title: "Awesome wiki_page",
+ content: "Some text describing some thing or another",
+ format: "md",
+ message: "user created page: Awesome wiki_page"
+ }
+
+ wiki_page_service = WikiPages::CreateService.new(project, user, opts)
+ @wiki_page = wiki_page_service.execute
+ @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create')
end
it "should call Slack API for push events" do
@@ -95,6 +106,12 @@ describe SlackService, models: true do
expect(WebMock).to have_requested(:post, webhook_url).once
end
+ it "should call Slack API for wiki page events" do
+ slack.execute(@wiki_page_sample_data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
it 'should use the username as an option for slack when configured' do
allow(slack).to receive(:username).and_return(username)
expect(Slack::Notifier).to receive(:new).
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index f26b47a856c..bc7423cee69 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -21,73 +21,220 @@
require 'spec_helper'
describe TeamcityService, models: true do
- describe "Associations" do
+ describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
- describe "Execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- context "when a password was previously set" do
- before do
- @teamcity_service = TeamcityService.create(
- project: create(:project),
- properties: {
- teamcity_url: 'http://gitlab.com',
- username: 'mic',
- password: "password"
- }
- )
+ describe 'Validations' do
+ describe '#teamcity_url' do
+ it 'does not validate the presence of teamcity_url if service is not active' do
+ teamcity_service = service
+ teamcity_service.active = false
+
+ expect(teamcity_service).not_to validate_presence_of(:teamcity_url)
end
-
- it "reset password if url changed" do
- @teamcity_service.teamcity_url = 'http://gitlab1.com'
- @teamcity_service.save
- expect(@teamcity_service.password).to be_nil
+
+ it 'validates the presence of teamcity_url if service is active' do
+ teamcity_service = service
+ teamcity_service.active = true
+
+ expect(teamcity_service).to validate_presence_of(:teamcity_url)
+ end
+ end
+
+ describe '#build_type' do
+ it 'does not validate the presence of build_type if service is not active' do
+ teamcity_service = service
+ teamcity_service.active = false
+
+ expect(teamcity_service).not_to validate_presence_of(:build_type)
+ end
+
+ it 'validates the presence of build_type if service is active' do
+ teamcity_service = service
+ teamcity_service.active = true
+
+ expect(teamcity_service).to validate_presence_of(:build_type)
end
-
- it "does not reset password if username changed" do
- @teamcity_service.username = "some_name"
- @teamcity_service.save
- expect(@teamcity_service.password).to eq("password")
+ end
+
+ describe '#username' do
+ it 'does not validate the presence of username if service is not active' do
+ teamcity_service = service
+ teamcity_service.active = false
+
+ expect(teamcity_service).not_to validate_presence_of(:username)
end
- it "does not reset password if new url is set together with password, even if it's the same password" do
- @teamcity_service.teamcity_url = 'http://gitlab_edited.com'
- @teamcity_service.password = 'password'
- @teamcity_service.save
- expect(@teamcity_service.password).to eq("password")
- expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com")
+ it 'does not validate the presence of username if username is nil' do
+ teamcity_service = service
+ teamcity_service.active = true
+ teamcity_service.password = nil
+
+ expect(teamcity_service).not_to validate_presence_of(:username)
end
- it "should reset password if url changed, even if setter called multiple times" do
- @teamcity_service.teamcity_url = 'http://gitlab1.com'
- @teamcity_service.teamcity_url = 'http://gitlab1.com'
- @teamcity_service.save
- expect(@teamcity_service.password).to be_nil
+ it 'validates the presence of username if service is active and username is present' do
+ teamcity_service = service
+ teamcity_service.active = true
+ teamcity_service.password = 'secret'
+
+ expect(teamcity_service).to validate_presence_of(:username)
end
end
-
- context "when no password was previously set" do
- before do
- @teamcity_service = TeamcityService.create(
- project: create(:project),
- properties: {
- teamcity_url: 'http://gitlab.com',
- username: 'mic'
- }
- )
+
+ describe '#password' do
+ it 'does not validate the presence of password if service is not active' do
+ teamcity_service = service
+ teamcity_service.active = false
+
+ expect(teamcity_service).not_to validate_presence_of(:password)
+ end
+
+ it 'does not validate the presence of password if username is nil' do
+ teamcity_service = service
+ teamcity_service.active = true
+ teamcity_service.username = nil
+
+ expect(teamcity_service).not_to validate_presence_of(:password)
end
- it "saves password if new url is set together with password" do
- @teamcity_service.teamcity_url = 'http://gitlab_edited.com'
- @teamcity_service.password = 'password'
- @teamcity_service.save
- expect(@teamcity_service.password).to eq("password")
- expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com")
+ it 'validates the presence of password if service is active and username is present' do
+ teamcity_service = service
+ teamcity_service.active = true
+ teamcity_service.username = 'john'
+
+ expect(teamcity_service).to validate_presence_of(:password)
end
end
end
+
+ describe 'Callbacks' do
+ describe 'before_update :reset_password' do
+ context 'when a password was previously set' do
+ it 'resets password if url changed' do
+ teamcity_service = service
+
+ teamcity_service.teamcity_url = 'http://gitlab1.com'
+ teamcity_service.save
+
+ expect(teamcity_service.password).to be_nil
+ end
+
+ it 'does not reset password if username changed' do
+ teamcity_service = service
+
+ teamcity_service.username = 'some_name'
+ teamcity_service.save
+
+ expect(teamcity_service.password).to eq('password')
+ end
+
+ it "does not reset password if new url is set together with password, even if it's the same password" do
+ teamcity_service = service
+
+ teamcity_service.teamcity_url = 'http://gitlab_edited.com'
+ teamcity_service.password = 'password'
+ teamcity_service.save
+
+ expect(teamcity_service.password).to eq('password')
+ expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
+ end
+ end
+
+ it 'saves password if new url is set together with password when no password was previously set' do
+ teamcity_service = service
+ teamcity_service.password = nil
+
+ teamcity_service.teamcity_url = 'http://gitlab_edited.com'
+ teamcity_service.password = 'password'
+ teamcity_service.save
+
+ expect(teamcity_service.password).to eq('password')
+ expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
+ end
+ end
+ end
+
+ describe '#build_page' do
+ it 'returns a specific URL when status is 500' do
+ stub_request(status: 500)
+
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo')
+ end
+
+ it 'returns a build URL when teamcity_url has no trailing slash' do
+ stub_request(body: %Q({"build":{"id":"666"}}))
+
+ expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+ end
+
+ it 'returns a build URL when teamcity_url has a trailing slash' do
+ stub_request(body: %Q({"build":{"id":"666"}}))
+
+ expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+ end
+ end
+
+ describe '#commit_status' do
+ it 'sets commit status to :error when status is 500' do
+ stub_request(status: 500)
+
+ expect(service.commit_status('123', 'unused')).to eq(:error)
+ end
+
+ it 'sets commit status to "pending" when status is 404' do
+ stub_request(status: 404)
+
+ expect(service.commit_status('123', 'unused')).to eq('pending')
+ end
+
+ it 'sets commit status to "success" when build status contains SUCCESS' do
+ stub_request(build_status: 'YAY SUCCESS!')
+
+ expect(service.commit_status('123', 'unused')).to eq('success')
+ end
+
+ it 'sets commit status to "failed" when build status contains FAILURE' do
+ stub_request(build_status: 'NO FAILURE!')
+
+ expect(service.commit_status('123', 'unused')).to eq('failed')
+ end
+
+ it 'sets commit status to "pending" when build status contains Pending' do
+ stub_request(build_status: 'NO Pending!')
+
+ expect(service.commit_status('123', 'unused')).to eq('pending')
+ end
+
+ it 'sets commit status to :error when build status is unknown' do
+ stub_request(build_status: 'FOO BAR!')
+
+ expect(service.commit_status('123', 'unused')).to eq(:error)
+ end
+ end
+
+ def service(teamcity_url: 'http://gitlab.com')
+ described_class.create(
+ project: build_stubbed(:empty_project),
+ properties: {
+ teamcity_url: teamcity_url,
+ username: 'mic',
+ password: 'password',
+ build_type: 'foo'
+ }
+ )
+ end
+
+ def stub_request(status: 200, body: nil, build_status: 'success')
+ teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
+ body ||= %Q({"build":{"status":"#{build_status}","id":"666"}})
+
+ WebMock.stub_request(:get, teamcity_full_url).to_return(
+ status: status,
+ headers: { 'Content-Type' => 'application/json' },
+ body: body
+ )
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 59df2c5cb87..7da26bb3d55 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -443,7 +443,20 @@ describe Project, models: true do
let(:project) { create :project }
let(:commit) { create :ci_commit, project: project, ref: 'master' }
- it { expect(project.ci_commit(commit.sha, 'master')).to eq(commit) }
+ subject { project.ci_commit(commit.sha, 'master') }
+
+ it { is_expected.to eq(commit) }
+
+ context 'return latest' do
+ let(:commit2) { create :ci_commit, project: project, ref: 'master' }
+
+ before do
+ commit
+ commit2
+ end
+
+ it { is_expected.to eq(commit2) }
+ end
end
describe :builds_enabled do
@@ -706,11 +719,8 @@ describe Project, models: true do
with('foo.wiki', project).
and_return(wiki)
- expect(repo).to receive(:expire_cache)
- expect(repo).to receive(:expire_emptiness_caches)
-
- expect(wiki).to receive(:expire_cache)
- expect(wiki).to receive(:expire_emptiness_caches)
+ expect(repo).to receive(:before_delete)
+ expect(wiki).to receive(:before_delete)
project.expire_caches_before_rename('foo')
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c3a4016fa49..c19524a01f8 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -94,6 +94,12 @@ describe Repository, models: true do
it { is_expected.to be_an Array }
+ it 'regex-escapes the query string' do
+ results = repository.search_files("test\\", 'master')
+
+ expect(results.first).not_to start_with('fatal:')
+ end
+
describe 'result' do
subject { results.first }
@@ -126,25 +132,56 @@ describe Repository, models: true do
it { expect(subject.basename).to eq('a/b/c') }
end
end
+ end
+
+ describe '#license_blob' do
+ before do
+ repository.send(:cache).expire(:license_blob)
+ repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
+ end
+
+ it 'looks in the root_ref only' do
+ repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'markdown')
+ repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'markdown', false)
+
+ expect(repository.license_blob).to be_nil
+ end
+
+ it 'detects license file with no recognizable open-source license content' do
+ repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false)
+
+ expect(repository.license_blob.name).to eq('LICENSE')
+ end
+ %w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md].each do |filename|
+ it "detects '#{filename}'" do
+ repository.commit_file(user, filename, Licensee::License.new('mit').content, "Add #{filename}", 'master', false)
+
+ expect(repository.license_blob.name).to eq(filename)
+ end
+ end
end
- describe "#license" do
+ describe '#license_key' do
before do
- repository.send(:cache).expire(:license)
+ repository.send(:cache).expire(:license_key)
+ repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
end
- it 'test selection preference' do
- files = [TestBlob.new('file'), TestBlob.new('license'), TestBlob.new('copying')]
- expect(repository.tree).to receive(:blobs).and_return(files)
+ it 'returns nil when no license is detected' do
+ expect(repository.license_key).to be_nil
+ end
+
+ it 'detects license file with no recognizable open-source license content' do
+ repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false)
- expect(repository.license.name).to eq('license')
+ expect(repository.license_key).to be_nil
end
- it 'also accepts licence instead of license' do
- expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('licence')])
+ it 'returns the license key' do
+ repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false)
- expect(repository.license.name).to eq('licence')
+ expect(repository.license_key).to eq('mit')
end
end
@@ -535,6 +572,41 @@ describe Repository, models: true do
end
end
+ describe '#cherry_pick' do
+ let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
+ let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
+ let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
+
+ context 'when there is a conflict' do
+ it 'should abort the operation' do
+ expect(repository.cherry_pick(user, conflict_commit, 'master')).to eq(false)
+ end
+ end
+
+ context 'when commit was already cherry-picked' do
+ it 'should abort the operation' do
+ repository.cherry_pick(user, pickable_commit, 'master')
+
+ expect(repository.cherry_pick(user, pickable_commit, 'master')).to eq(false)
+ end
+ end
+
+ context 'when commit can be cherry-picked' do
+ it 'should cherry-pick the changes' do
+ expect(repository.cherry_pick(user, pickable_commit, 'master')).to be_truthy
+ end
+ end
+
+ context 'cherry-picking a merge commit' do
+ it 'should cherry-pick the changes' do
+ expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).to be_nil
+
+ repository.cherry_pick(user, pickable_merge, 'master')
+ expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).not_to be_nil
+ end
+ end
+ end
+
describe '#before_delete' do
describe 'when a repository does not exist' do
before do
@@ -764,11 +836,9 @@ describe Repository, models: true do
describe '#rm_tag' do
it 'removes a tag' do
expect(repository).to receive(:before_remove_tag)
+ expect(repository.rugged.tags).to receive(:delete).with('v1.1.0')
- expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag).
- with(repository.path_with_namespace, '8.5')
-
- repository.rm_tag('8.5')
+ repository.rm_tag('v1.1.0')
end
end
@@ -906,9 +976,32 @@ describe Repository, models: true do
end
end
+ describe '.clean_old_archives' do
+ let(:path) { Gitlab.config.gitlab.repository_downloads_path }
+
+ context 'when the downloads directory does not exist' do
+ it 'does not remove any archives' do
+ expect(File).to receive(:directory?).with(path).and_return(false)
+
+ expect(Gitlab::Popen).not_to receive(:popen)
+
+ described_class.clean_old_archives
+ end
+ end
+
+ context 'when the downloads directory exists' do
+ it 'removes old archives' do
+ expect(File).to receive(:directory?).with(path).and_return(true)
+
+ expect(Gitlab::Popen).to receive(:popen)
+
+ described_class.clean_old_archives
+ end
+ 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/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 25377a40442..e28998d51b5 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -48,7 +48,7 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
- it "should return not_found for CI status" do
+ it "should return nil for commit without CI" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
expect(response.status).to eq(200)
expect(json_response['status']).to be_nil
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 3d7a31cbb6a..f88e39cad9e 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
+ let(:user2) { create(:user) }
let(:non_member) { create(:user) }
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
@@ -320,13 +321,13 @@ describe API::API, api: true do
end
context 'when an admin or owner makes the request' do
- it "accepts the creation date to be set" do
+ it 'accepts the creation date to be set' do
+ creation_time = 2.weeks.ago
post api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: 'label, label2', created_at: 2.weeks.ago
+ title: 'new issue', labels: 'label, label2', created_at: creation_time
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
+ expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
end
end
end
@@ -477,6 +478,18 @@ describe API::API, api: true do
expect(json_response['labels']).to include 'label2'
expect(json_response['state']).to eq "closed"
end
+
+ context 'when an admin or owner makes the request' do
+ it 'accepts the update date to be set' do
+ update_time = 2.weeks.ago
+ put api("/projects/#{project.id}/issues/#{issue.id}", user),
+ labels: 'label3', state_event: 'close', updated_at: update_time
+ expect(response.status).to eq(200)
+
+ expect(json_response['labels']).to include 'label3'
+ expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time)
+ end
+ end
end
describe "DELETE /projects/:id/issues/:issue_id" do
@@ -569,4 +582,46 @@ describe API::API, api: true do
end
end
end
+
+ describe 'POST :id/issues/:issue_id/subscription' do
+ it 'subscribes to an issue' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)
+
+ expect(response.status).to eq(201)
+ expect(json_response['subscribed']).to eq(true)
+ end
+
+ it 'returns 304 if already subscribed' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+
+ it 'returns 404 if the issue is not found' do
+ post api("/projects/#{project.id}/issues/123/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ describe 'DELETE :id/issues/:issue_id/subscription' do
+ it 'unsubscribes from an issue' do
+ delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response['subscribed']).to eq(false)
+ end
+
+ it 'returns 304 if not subscribed' do
+ delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2)
+
+ expect(response.status).to eq(304)
+ end
+
+ it 'returns 404 if the issue is not found' do
+ delete api("/projects/#{project.id}/issues/123/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
end
diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/licenses_spec.rb
new file mode 100644
index 00000000000..c17dcb222a9
--- /dev/null
+++ b/spec/requests/api/licenses_spec.rb
@@ -0,0 +1,136 @@
+require 'spec_helper'
+
+describe API::Licenses, api: true do
+ include ApiHelpers
+
+ describe 'Entity' do
+ before { get api('/licenses/mit') }
+
+ it { expect(json_response['key']).to eq('mit') }
+ it { expect(json_response['name']).to eq('MIT License') }
+ it { expect(json_response['nickname']).to be_nil }
+ it { expect(json_response['popular']).to be true }
+ it { expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') }
+ it { expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') }
+ it { expect(json_response['description']).to include('A permissive license that is short and to the point.') }
+ it { expect(json_response['conditions']).to eq(%w[include-copyright]) }
+ it { expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) }
+ it { expect(json_response['limitations']).to eq(%w[no-liability]) }
+ it { expect(json_response['content']).to include('The MIT License (MIT)') }
+ end
+
+ describe 'GET /licenses' do
+ it 'returns a list of available license templates' do
+ get api('/licenses')
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(15)
+ expect(json_response.map { |l| l['key'] }).to include('agpl-3.0')
+ end
+
+ describe 'the popular parameter' do
+ context 'with popular=1' do
+ it 'returns a list of available popular license templates' do
+ get api('/licenses?popular=1')
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(3)
+ expect(json_response.map { |l| l['key'] }).to include('apache-2.0')
+ end
+ end
+ end
+ end
+
+ describe 'GET /licenses/:key' do
+ context 'with :project and :fullname given' do
+ before do
+ get api("/licenses/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}")
+ end
+
+ context 'for the mit license' do
+ let(:license_type) { 'mit' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('The MIT License (MIT)')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('Copyright (c) 2016 Anton')
+ end
+ end
+
+ context 'for the agpl-3.0 license' do
+ let(:license_type) { 'agpl-3.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ end
+ end
+
+ context 'for the gpl-3.0 license' do
+ let(:license_type) { 'gpl-3.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ end
+ end
+
+ context 'for the gpl-2.0 license' do
+ let(:license_type) { 'gpl-2.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('My Awesome Project')
+ expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ end
+ end
+
+ context 'for the apache-2.0 license' do
+ let(:license_type) { 'apache-2.0' }
+
+ it 'returns the license text' do
+ expect(json_response['content']).to include('Apache License')
+ end
+
+ it 'replaces placeholder values' do
+ expect(json_response['content']).to include('Copyright 2016 Anton')
+ end
+ end
+
+ context 'for an uknown license' do
+ let(:license_type) { 'muth-over9000' }
+
+ it 'returns a 404' do
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
+ context 'with no :fullname given' do
+ context 'with an authenticated user' do
+ let(:user) { create(:user) }
+
+ it 'replaces the copyright owner placeholder with the name of the current user' do
+ get api('/licenses/mit', user)
+
+ expect(json_response['content']).to include("Copyright (c) 2016 #{user.name}")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 25fa30b2f21..1fa7e76894f 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -516,6 +516,48 @@ describe API::API, api: true do
end
end
+ describe 'POST :id/merge_requests/:merge_request_id/subscription' do
+ it 'subscribes to a merge request' do
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin)
+
+ expect(response.status).to eq(201)
+ expect(json_response['subscribed']).to eq(true)
+ end
+
+ it 'returns 304 if already subscribed' do
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+
+ it 'returns 404 if the merge request is not found' do
+ post api("/projects/#{project.id}/merge_requests/123/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ describe 'DELETE :id/merge_requests/:merge_request_id/subscription' do
+ it 'unsubscribes from a merge request' do
+ delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response['subscribed']).to eq(false)
+ end
+
+ it 'returns 304 if not subscribed' do
+ delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin)
+
+ expect(response.status).to eq(304)
+ end
+
+ it 'returns 404 if the merge request is not found' do
+ post api("/projects/#{project.id}/merge_requests/123/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
def mr_with_later_created_and_updated_at_time
merge_request
merge_request.created_at += 1.hour
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index a467bc935af..ec9eda0a2ed 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -158,6 +158,19 @@ describe API::API, api: true do
post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
expect(response.status).to eq(401)
end
+
+ context 'when an admin or owner makes the request' do
+ it 'accepts the creation date to be set' do
+ creation_time = 2.weeks.ago
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
+ body: 'hi!', created_at: creation_time
+ expect(response.status).to eq(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
+ expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
+ end
+ end
+
end
context "when noteable is a Snippet" do
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index 142b637d291..ffb93bbb120 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -148,14 +148,24 @@ describe API::API, 'ProjectHooks', api: true do
expect(response.status).to eq(200)
end
- it "should return success when deleting non existent hook" do
+ it "should return a 404 error when deleting non existent hook" do
delete api("/projects/#{project.id}/hooks/42", user)
- expect(response.status).to eq(200)
+ expect(response.status).to eq(404)
end
it "should return a 405 error if hook id not given" do
delete api("/projects/#{project.id}/hooks", user)
expect(response.status).to eq(405)
end
+
+ it "shold return a 404 if a user attempts to delete project hooks he/she does not own" do
+ test_user = create(:user)
+ other_project = create(:project)
+ other_project.team << [test_user, :master]
+
+ delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
+ expect(response.status).to eq(404)
+ expect(WebHook.exists?(hook.id)).to be_truthy
+ end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index be2034e0f39..fccd08bd6da 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1020,6 +1020,54 @@ describe API::API, api: true do
end
end
+ describe 'POST /projects/:id/star' do
+ context 'on an unstarred project' do
+ it 'stars the project' do
+ expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
+
+ expect(response.status).to eq(201)
+ expect(json_response['star_count']).to eq(1)
+ end
+ end
+
+ context 'on a starred project' do
+ before do
+ user.toggle_star(project)
+ project.reload
+ end
+
+ it 'does not modify the star count' do
+ expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
+
+ expect(response.status).to eq(304)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id/star' do
+ context 'on a starred project' do
+ before do
+ user.toggle_star(project)
+ project.reload
+ end
+
+ it 'unstars the project' do
+ expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1)
+
+ expect(response.status).to eq(200)
+ expect(json_response['star_count']).to eq(0)
+ end
+ end
+
+ context 'on an unstarred project' do
+ it 'does not modify the star count' do
+ expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
+
+ expect(response.status).to eq(304)
+ 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 9f9c3b1cf4c..edcb2bedbf7 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -32,9 +32,11 @@ describe API::API, api: true do
it "should return an array of project tags with release info" do
get api("/projects/#{project.id}/repository/tags", user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
+ expect(json_response.first['message']).to eq('Version 1.1.0')
expect(json_response.first['release']['description']).to eq(description)
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 679227bf881..40b24c125b5 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -20,6 +20,24 @@ describe API::API, api: true do
end
context "when authenticated" do
+ #These specs are written just in case API authentication is not required anymore
+ context "when public level is restricted" do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true)
+ end
+
+ it "renders 403" do
+ get api("/users")
+ expect(response.status).to eq(403)
+ end
+
+ it "renders 404" do
+ get api("/users/#{user.id}")
+ expect(response.status).to eq(404)
+ end
+ end
+
it "should return an array of users" do
get api("/users", user)
expect(response.status).to eq(200)
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index b652b488b5a..dfd361a2cdd 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -156,6 +156,52 @@ describe Ci::API::API do
end
end
+ describe 'PATCH /builds/:id/trace.txt' do
+ let(:build) { create(:ci_build, :trace, runner_id: runner.id) }
+ let(:headers) { { Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token, 'Content-Type' => 'text/plain' } }
+ let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) }
+
+ before do
+ build.run!
+ patch ci_api("/builds/#{build.id}/trace.txt"), ' appended', headers_with_range
+ end
+
+ context 'when request is valid' do
+ it { expect(response.status).to eq 202 }
+ it { expect(build.reload.trace).to eq 'BUILD TRACE appended' }
+ it { expect(response.header).to have_key 'Range' }
+ it { expect(response.header).to have_key 'Build-Status' }
+ end
+
+ context 'when content-range start is too big' do
+ let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) }
+
+ it { expect(response.status).to eq 416 }
+ it { expect(response.header).to have_key 'Range' }
+ it { expect(response.header['Range']).to eq '0-11' }
+ end
+
+ context 'when content-range start is too small' do
+ let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) }
+
+ it { expect(response.status).to eq 416 }
+ it { expect(response.header).to have_key 'Range' }
+ it { expect(response.header['Range']).to eq '0-11' }
+ end
+
+ context 'when Content-Range header is missing' do
+ let(:headers_with_range) { headers.merge({}) }
+
+ it { expect(response.status).to eq 400 }
+ end
+
+ context 'when build has been errased' do
+ let(:build) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) }
+
+ it { expect(response.status).to eq 403 }
+ end
+ end
+
context "Artifacts" do
let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/delete_tag_service_spec.rb
index 5b7ba521812..477551f5036 100644
--- a/spec/services/delete_tag_service_spec.rb
+++ b/spec/services/delete_tag_service_spec.rb
@@ -6,21 +6,12 @@ describe DeleteTagService, services: true do
let(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
- let(:tag) { double(:tag, name: '8.5', target: 'abc123') }
-
describe '#execute' do
- before do
- allow(repository).to receive(:find_tag).and_return(tag)
- end
-
it 'removes the tag' do
- expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag).
- and_return(true)
-
expect(repository).to receive(:before_remove_tag)
expect(service).to receive(:success)
- service.execute('8.5')
+ service.execute('v1.1.0')
end
end
end
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index cc780587e74..a63656e6268 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -5,19 +5,17 @@ describe GitTagPushService, services: true do
let(:user) { create :user }
let(:project) { create :project }
- let(:service) { GitTagPushService.new }
+ let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) }
- before do
- @oldrev = Gitlab::Git::BLANK_SHA
- @newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0
- @ref = 'refs/tags/v1.1.0'
- end
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+ let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0
+ let(:ref) { 'refs/tags/v1.1.0' }
describe "Git Tag Push Data" do
before do
- service.execute(project, user, @oldrev, @newrev, @ref)
+ service.execute
@push_data = service.push_data
- @tag_name = Gitlab::Git.ref_name(@ref)
+ @tag_name = Gitlab::Git.ref_name(ref)
@tag = project.repository.find_tag(@tag_name)
@commit = project.commit(@tag.target)
end
@@ -25,9 +23,9 @@ describe GitTagPushService, services: true do
subject { @push_data }
it { is_expected.to include(object_kind: 'tag_push') }
- it { is_expected.to include(ref: @ref) }
- it { is_expected.to include(before: @oldrev) }
- it { is_expected.to include(after: @newrev) }
+ it { is_expected.to include(ref: ref) }
+ it { is_expected.to include(before: oldrev) }
+ it { is_expected.to include(after: newrev) }
it { is_expected.to include(message: @tag.message) }
it { is_expected.to include(user_id: user.id) }
it { is_expected.to include(user_name: user.name) }
@@ -80,9 +78,11 @@ describe GitTagPushService, services: true do
describe "Webhooks" do
context "execute webhooks" do
+ let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') }
+
it "when pushing tags" do
expect(project).to receive(:execute_hooks)
- service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0')
+ service.execute
end
end
end
diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb
index 6a7ea4b2f44..e91906d0d49 100644
--- a/spec/services/issues/bulk_update_service_spec.rb
+++ b/spec/services/issues/bulk_update_service_spec.rb
@@ -100,7 +100,7 @@ describe Issues::BulkUpdateService, services: true do
describe :update_milestone do
before do
- @milestone = create :milestone
+ @milestone = create(:milestone, project: @project)
@params = {
issues_ids: [issue.id],
milestone_id: @milestone.id
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 5e7915db7e1..ac28b6f71f9 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -3,40 +3,75 @@ require 'spec_helper'
describe Issues::CreateService, services: true do
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
- let(:assignee) { create(:user) }
- describe :execute do
- context 'valid params' do
+ describe '#execute' do
+ let(:issue) { described_class.new(project, user, opts).execute }
+
+ context 'when params are valid' do
+ let(:assignee) { create(:user) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:labels) { create_pair(:label, project: project) }
+
before do
project.team << [user, :master]
project.team << [assignee, :master]
+ end
- opts = {
- title: 'Awesome issue',
+ let(:opts) do
+ { title: 'Awesome issue',
description: 'please fix',
- assignee: assignee
- }
-
- @issue = Issues::CreateService.new(project, user, opts).execute
+ assignee: assignee,
+ label_ids: labels.map(&:id),
+ milestone_id: milestone.id }
end
- it { expect(@issue).to be_valid }
- it { expect(@issue.title).to eq('Awesome issue') }
- it { expect(@issue.assignee).to eq assignee }
+ it { expect(issue).to be_valid }
+ it { expect(issue.title).to eq('Awesome issue') }
+ it { expect(issue.assignee).to eq assignee }
+ it { expect(issue.labels).to match_array labels }
+ it { expect(issue.milestone).to eq milestone }
it 'creates a pending todo for new assignee' do
attributes = {
project: project,
author: user,
user: assignee,
- target_id: @issue.id,
- target_type: @issue.class.name,
+ target_id: issue.id,
+ target_type: issue.class.name,
action: Todo::ASSIGNED,
state: :pending
}
expect(Todo.where(attributes).count).to eq 1
end
+
+ context 'when label belongs to different project' do
+ let(:label) { create(:label) }
+
+ let(:opts) do
+ { title: 'Title',
+ description: 'Description',
+ label_ids: [label.id] }
+ end
+
+ it 'does not assign label'do
+ expect(issue.labels).to_not include label
+ end
+ end
+
+ context 'when milestone belongs to different project' do
+ let(:milestone) { create(:milestone) }
+
+ let(:opts) do
+ { title: 'Title',
+ description: 'Description',
+ milestone_id: milestone.id }
+ end
+
+ it 'does not assign milestone' do
+ expect(issue.milestone).to_not eq milestone
+ end
+ end
end
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 6b214a0d96b..52f69306994 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -4,10 +4,15 @@ describe Issues::UpdateService, services: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
- let(:issue) { create(:issue, title: 'Old title', assignee_id: user3.id) }
- let(:label) { create(:label) }
+ let(:project) { create(:empty_project) }
+ let(:label) { create(:label, project: project) }
let(:label2) { create(:label) }
- let(:project) { issue.project }
+
+ let(:issue) do
+ create(:issue, title: 'Old title',
+ assignee_id: user3.id,
+ project: project)
+ end
before do
project.team << [user, :master]
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index cb8cff2fa8c..213e8c2eb3a 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -1,14 +1,19 @@
require 'spec_helper'
describe MergeRequests::UpdateService, services: true do
+ let(:project) { create(:project) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
- let(:merge_request) { create(:merge_request, :simple, title: 'Old title', assignee_id: user3.id) }
- let(:project) { merge_request.project }
- let(:label) { create(:label) }
+ let(:label) { create(:label, project: project) }
let(:label2) { create(:label) }
+ let(:merge_request) do
+ create(:merge_request, :simple, title: 'Old title',
+ assignee_id: user3.id,
+ source_project: project)
+ end
+
before do
project.team << [user, :master]
project.team << [user2, :developer]
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index c46259431aa..06017317339 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -38,4 +38,27 @@ describe Projects::TransferService, services: true do
def transfer_project(project, user, new_namespace)
Projects::TransferService.new(project, user).execute(new_namespace)
end
+
+ context 'visibility level' do
+ let(:internal_group) { create(:group, :internal) }
+
+ before { internal_group.add_owner(user) }
+
+ context 'when namespace visibility level < project visibility level' do
+ let(:public_project) { create(:project, :public, namespace: user.namespace) }
+
+ before { transfer_project(public_project, user, internal_group) }
+
+ it { expect(public_project.visibility_level).to eq(internal_group.visibility_level) }
+ end
+
+ context 'when namespace visibility level > project visibility level' do
+ let(:private_project) { create(:project, :private, namespace: user.namespace) }
+
+ before { transfer_project(private_project, user, internal_group) }
+
+ it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) }
+ end
+ end
+
end
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index a5b256bd3ec..e55a61b2b94 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -4,7 +4,7 @@ services:
before_script:
- gem install bundler
- - bundle install
+ - bundle install
- bundle exec rake db:create
variables:
@@ -17,7 +17,7 @@ types:
rspec:
script: "rake spec"
- tags:
+ tags:
- ruby
- postgres
only:
@@ -26,27 +26,32 @@ rspec:
spinach:
script: "rake spinach"
allow_failure: true
- tags:
+ tags:
- ruby
- mysql
except:
- tags
staging:
+ variables:
+ KEY1: value1
+ KEY2: value2
script: "cap deploy stating"
type: deploy
- tags:
+ tags:
- ruby
- mysql
except:
- stable
production:
+ variables:
+ DB_NAME: mysql
type: deploy
- script:
+ script:
- cap deploy production
- cap notify
- tags:
+ tags:
- ruby
- mysql
only:
diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb
index 422083875d7..7dbaa6a6459 100644
--- a/spec/support/project_hook_data_shared_example.rb
+++ b/spec/support/project_hook_data_shared_example.rb
@@ -1,4 +1,4 @@
-RSpec.shared_examples 'project hook data' do |project_key: :project|
+RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :project|
it 'contains project data' do
expect(data[project_key][:name]).to eq(project.name)
expect(data[project_key][:description]).to eq(project.description)
@@ -17,6 +17,21 @@ RSpec.shared_examples 'project hook data' do |project_key: :project|
end
end
+RSpec.shared_examples 'project hook data' do |project_key: :project|
+ it 'contains project data' do
+ expect(data[project_key][:name]).to eq(project.name)
+ expect(data[project_key][:description]).to eq(project.description)
+ expect(data[project_key][:web_url]).to eq(project.web_url)
+ expect(data[project_key][:avatar_url]).to eq(project.avatar_url)
+ expect(data[project_key][:git_http_url]).to eq(project.http_url_to_repo)
+ expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo)
+ expect(data[project_key][:namespace]).to eq(project.namespace.name)
+ expect(data[project_key][:visibility_level]).to eq(project.visibility_level)
+ expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace)
+ expect(data[project_key][:default_branch]).to eq(project.default_branch)
+ end
+end
+
RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project|
it 'contains deprecated repository data' do
expect(data[:repository][:name]).to eq(project.name)
diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb
index aa8258d6dad..73f375c481b 100644
--- a/spec/support/repo_helpers.rb
+++ b/spec/support/repo_helpers.rb
@@ -42,7 +42,7 @@ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
eos
)
end
-
+
def another_sample_commit
OpenStruct.new(
id: "e56497bb5f03a90a51293fc6d516788730953899",
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 0265dbe9c66..94ff3457902 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -4,6 +4,9 @@ describe PostReceive do
let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
+ let(:project) { create(:project) }
+ let(:key) { create(:key, user: project.owner) }
+ let(:key_id) { key.shell_id }
context "as a resque worker" do
it "reponds to #perform" do
@@ -11,11 +14,43 @@ describe PostReceive do
end
end
- context "webhook" do
- let(:project) { create(:project) }
- let(:key) { create(:key, user: project.owner) }
- let(:key_id) { key.shell_id }
+ describe "#process_project_changes" do
+ before do
+ allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
+ end
+ context "branches" do
+ let(:changes) { "123456 789012 refs/heads/tést" }
+
+ it "should call GitTagPushService" do
+ expect_any_instance_of(GitPushService).to receive(:execute).and_return(true)
+ expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ PostReceive.new.perform(pwd(project), key_id, base64_changes)
+ end
+ end
+
+ context "tags" do
+ let(:changes) { "123456 789012 refs/tags/tag" }
+
+ it "should call GitTagPushService" do
+ expect_any_instance_of(GitPushService).not_to receive(:execute)
+ expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true)
+ PostReceive.new.perform(pwd(project), key_id, base64_changes)
+ end
+ end
+
+ context "merge-requests" do
+ let(:changes) { "123456 789012 refs/merge-requests/123" }
+
+ it "should not call any of the services" do
+ expect_any_instance_of(GitPushService).not_to receive(:execute)
+ expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ PostReceive.new.perform(pwd(project), key_id, base64_changes)
+ end
+ end
+ end
+
+ context "webhook" do
it "fetches the correct project" do
expect(Project).to receive(:find_with_namespace).with(project.path_with_namespace).and_return(project)
PostReceive.new.perform(pwd(project), key_id, base64_changes)
diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb
new file mode 100644
index 00000000000..f486e45ddad
--- /dev/null
+++ b/spec/workers/repository_check/batch_worker_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe RepositoryCheck::BatchWorker do
+ subject { described_class.new }
+
+ it 'prefers projects that have never been checked' do
+ projects = create_list(:project, 3)
+ projects[0].update_column(:last_repository_check_at, 4.months.ago)
+ projects[2].update_column(:last_repository_check_at, 3.months.ago)
+
+ expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id))
+ end
+
+ it 'sorts projects by last_repository_check_at' do
+ projects = create_list(:project, 3)
+ projects[0].update_column(:last_repository_check_at, 2.months.ago)
+ projects[1].update_column(:last_repository_check_at, 4.months.ago)
+ projects[2].update_column(:last_repository_check_at, 3.months.ago)
+
+ expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id))
+ end
+
+ it 'excludes projects that were checked recently' do
+ projects = create_list(:project, 3)
+ projects[0].update_column(:last_repository_check_at, 2.days.ago)
+ projects[1].update_column(:last_repository_check_at, 2.months.ago)
+ projects[2].update_column(:last_repository_check_at, 3.days.ago)
+
+ expect(subject.perform).to eq([projects[1].id])
+ end
+
+ it 'does nothing when repository checks are disabled' do
+ create(:empty_project)
+ current_settings = double('settings', repository_checks_enabled: false)
+ expect(subject).to receive(:current_settings) { current_settings }
+
+ expect(subject.perform).to eq(nil)
+ end
+end
diff --git a/spec/workers/repository_check/clear_worker_spec.rb b/spec/workers/repository_check/clear_worker_spec.rb
new file mode 100644
index 00000000000..a3b70c74787
--- /dev/null
+++ b/spec/workers/repository_check/clear_worker_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe RepositoryCheck::ClearWorker do
+ it 'clears repository check columns' do
+ project = create(:empty_project)
+ project.update_columns(
+ last_repository_check_failed: true,
+ last_repository_check_at: Time.now,
+ )
+
+ described_class.new.perform
+ project.reload
+
+ expect(project.last_repository_check_failed).to be_nil
+ expect(project.last_repository_check_at).to be_nil
+ end
+end
diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb
new file mode 100644
index 00000000000..087e4c667d8
--- /dev/null
+++ b/spec/workers/repository_check/single_repository_worker_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'fileutils'
+
+describe RepositoryCheck::SingleRepositoryWorker do
+ subject { described_class.new }
+
+ it 'fails if the wiki repository is broken' do
+ project = create(:project_empty_repo, wiki_enabled: true)
+ project.create_wiki
+
+ # Test sanity: everything should be fine before the wiki repo is broken
+ subject.perform(project.id)
+ expect(project.reload.last_repository_check_failed).to eq(false)
+
+ destroy_wiki(project)
+ subject.perform(project.id)
+
+ expect(project.reload.last_repository_check_failed).to eq(true)
+ end
+
+ it 'skips wikis when disabled' do
+ project = create(:project_empty_repo, wiki_enabled: false)
+ # Make sure the test would fail if it checked the wiki repo
+ destroy_wiki(project)
+
+ subject.perform(project.id)
+
+ expect(project.reload.last_repository_check_failed).to eq(false)
+ end
+
+ def destroy_wiki(project)
+ FileUtils.rm_rf(project.wiki.repository.path_to_repo)
+ end
+end