Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb13
-rw-r--r--spec/controllers/help_controller_spec.rb24
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb60
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb4
-rw-r--r--spec/factories/ci/pipelines.rb28
-rw-r--r--spec/features/admin/admin_browses_logs_spec.rb15
-rw-r--r--spec/features/admin/admin_hooks_spec.rb15
-rw-r--r--spec/features/admin/admin_users_spec.rb28
-rw-r--r--spec/features/boards/sidebar_spec.rb4
-rw-r--r--spec/features/commits_spec.rb19
-rw-r--r--spec/features/groups/labels/edit_spec.rb21
-rw-r--r--spec/features/groups/merge_requests_spec.rb30
-rw-r--r--spec/features/help_pages_spec.rb13
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb76
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/features/merge_requests/conflicts_spec.rb2
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb2
-rw-r--r--spec/features/merge_requests/deleted_source_branch_spec.rb3
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb (renamed from spec/features/merge_requests/merge_when_build_succeeds_spec.rb)31
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb4
-rw-r--r--spec/features/merge_requests/target_branch_spec.rb41
-rw-r--r--spec/features/profiles/preferences_spec.rb2
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb4
-rw-r--r--spec/features/projects/members/group_members_spec.rb90
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb154
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb (renamed from spec/features/projects/pipelines_spec.rb)59
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb12
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb4
-rw-r--r--spec/features/security/group/internal_access_spec.rb123
-rw-r--r--spec/features/security/group/private_access_spec.rb123
-rw-r--r--spec/features/security/group/public_access_spec.rb123
-rw-r--r--spec/features/security/project/private_access_spec.rb55
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb124
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb86
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb162
-rw-r--r--spec/finders/issues_finder_spec.rb37
-rw-r--r--spec/finders/labels_finder_spec.rb2
-rw-r--r--spec/finders/merge_requests_finder_spec.rb11
-rw-r--r--spec/fixtures/api/schemas/user/login.json37
-rw-r--r--spec/fixtures/api/schemas/user/public.json79
-rw-r--r--spec/helpers/labels_helper_spec.rb4
-rw-r--r--spec/javascripts/application_spec.js37
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js.es659
-rw-r--r--spec/javascripts/environments/environment_item_spec.js.es614
-rw-r--r--spec/javascripts/extensions/element_spec.js.es638
-rw-r--r--spec/javascripts/fixtures/application.html.haml2
-rw-r--r--spec/javascripts/fixtures/comments.html.haml21
-rw-r--r--spec/javascripts/fixtures/issuable.html.haml2
-rw-r--r--spec/javascripts/fixtures/issue_note.html.haml12
-rw-r--r--spec/javascripts/fixtures/issues.rb7
-rw-r--r--spec/javascripts/fixtures/linked_tabs.html.haml13
-rw-r--r--spec/javascripts/fixtures/right_sidebar.html.haml17
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js.es632
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js120
-rw-r--r--spec/javascripts/notes_spec.js21
-rw-r--r--spec/javascripts/right_sidebar_spec.js14
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js6
-rw-r--r--spec/javascripts/vue_common_components/commit_spec.js.es610
-rw-r--r--spec/lib/banzai/filter/abstract_link_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb99
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb78
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb108
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb291
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb92
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb108
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb85
-rw-r--r--spec/lib/banzai/filter/table_of_contents_filter_spec.rb21
-rw-r--r--spec/lib/gitlab/backup/manager_spec.rb127
-rw-r--r--spec/lib/gitlab/chat_commands/issue_create_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/status/canceled_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/created_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/extended_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/status/failed_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/pending_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/common_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb65
-rw-r--r--spec/lib/gitlab/ci/status/running_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/skipped_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/status/success_spec.rb21
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb8
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb25
-rw-r--r--spec/lib/gitlab/github_import/branch_formatter_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb11
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb194
-rw-r--r--spec/models/build_spec.rb4
-rw-r--r--spec/models/ci/pipeline_spec.rb72
-rw-r--r--spec/models/ci/variable_spec.rb7
-rw-r--r--spec/models/commit_range_spec.rb8
-rw-r--r--spec/models/commit_spec.rb31
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/discussion_spec.rb9
-rw-r--r--spec/models/environment_spec.rb4
-rw-r--r--spec/models/group_label_spec.rb10
-rw-r--r--spec/models/issue_spec.rb10
-rw-r--r--spec/models/key_spec.rb8
-rw-r--r--spec/models/merge_request_diff_spec.rb34
-rw-r--r--spec/models/merge_request_spec.rb102
-rw-r--r--spec/models/milestone_spec.rb14
-rw-r--r--spec/models/namespace_spec.rb13
-rw-r--r--spec/models/project_label_spec.rb4
-rw-r--r--spec/models/project_services/jira_service_spec.rb40
-rw-r--r--spec/models/project_services/pipeline_email_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb76
-rw-r--r--spec/models/repository_spec.rb35
-rw-r--r--spec/models/snippet_spec.rb51
-rw-r--r--spec/models/user_spec.rb19
-rw-r--r--spec/policies/project_policy_spec.rb38
-rw-r--r--spec/rake_helper.rb2
-rw-r--r--spec/requests/api/api_helpers_spec.rb253
-rw-r--r--spec/requests/api/award_emoji_spec.rb2
-rw-r--r--spec/requests/api/boards_spec.rb2
-rw-r--r--spec/requests/api/branches_spec.rb13
-rw-r--r--spec/requests/api/builds_spec.rb4
-rw-r--r--spec/requests/api/commits_spec.rb11
-rw-r--r--spec/requests/api/deploy_keys_spec.rb6
-rw-r--r--spec/requests/api/deployments_spec.rb2
-rw-r--r--spec/requests/api/environments_spec.rb2
-rw-r--r--spec/requests/api/files_spec.rb2
-rw-r--r--spec/requests/api/groups_spec.rb2
-rw-r--r--spec/requests/api/internal_spec.rb2
-rw-r--r--spec/requests/api/issues_spec.rb55
-rw-r--r--spec/requests/api/keys_spec.rb2
-rw-r--r--spec/requests/api/labels_spec.rb2
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb6
-rw-r--r--spec/requests/api/milestones_spec.rb2
-rw-r--r--spec/requests/api/namespaces_spec.rb2
-rw-r--r--spec/requests/api/notes_spec.rb2
-rw-r--r--spec/requests/api/notification_settings_spec.rb2
-rw-r--r--spec/requests/api/pipelines_spec.rb2
-rw-r--r--spec/requests/api/project_hooks_spec.rb2
-rw-r--r--spec/requests/api/project_snippets_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb419
-rw-r--r--spec/requests/api/repositories_spec.rb5
-rw-r--r--spec/requests/api/services_spec.rb2
-rw-r--r--spec/requests/api/session_spec.rb2
-rw-r--r--spec/requests/api/settings_spec.rb2
-rw-r--r--spec/requests/api/system_hooks_spec.rb2
-rw-r--r--spec/requests/api/tags_spec.rb57
-rw-r--r--spec/requests/api/triggers_spec.rb2
-rw-r--r--spec/requests/api/users_spec.rb81
-rw-r--r--spec/requests/api/variables_spec.rb2
-rw-r--r--spec/requests/api/version_spec.rb2
-rw-r--r--spec/requests/ci/api/builds_spec.rb2
-rw-r--r--spec/requests/ci/api/runners_spec.rb2
-rw-r--r--spec/requests/ci/api/triggers_spec.rb2
-rw-r--r--spec/requests/projects/cycle_analytics_events_spec.rb6
-rw-r--r--spec/serializers/analytics_build_entity_spec.rb53
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb137
-rw-r--r--spec/services/discussions/resolve_service_spec.rb52
-rw-r--r--spec/services/git_push_service_spec.rb6
-rw-r--r--spec/services/issues/build_service_spec.rb130
-rw-r--r--spec/services/issues/create_service_spec.rb43
-rw-r--r--spec/services/issues/move_service_spec.rb4
-rw-r--r--spec/services/issues/update_service_spec.rb2
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb12
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb (renamed from spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb)38
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb2
-rw-r--r--spec/services/projects/create_service_spec.rb212
-rw-r--r--spec/services/projects/destroy_service_spec.rb30
-rw-r--r--spec/services/system_note_service_spec.rb36
-rw-r--r--spec/spec_helper.rb6
-rw-r--r--spec/support/matchers/access_matchers.rb31
-rw-r--r--spec/support/matchers/have_issuable_counts.rb8
-rw-r--r--spec/support/matchers/is_within.rb9
-rw-r--r--spec/support/matchers/markdown_matchers.rb6
-rw-r--r--spec/support/rake_helpers.rb4
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb33
-rw-r--r--spec/tasks/gitlab/mail_google_schema_whitelisting.rb2
-rw-r--r--spec/tasks/gitlab/task_helpers_spec.rb96
-rw-r--r--spec/tasks/gitlab/users_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/workhorse_rake_spec.rb81
-rw-r--r--spec/workers/build_email_worker_spec.rb1
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb1
-rw-r--r--spec/workers/pipeline_notification_worker_spec.rb4
-rw-r--r--spec/workers/pipeline_success_worker_spec.rb2
-rw-r--r--spec/workers/process_commit_worker_spec.rb29
184 files changed, 4828 insertions, 1543 deletions
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index d9a86346c81..ea2fd90a9b0 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -4,7 +4,7 @@ describe AutocompleteController do
let!(:project) { create(:project) }
let!(:user) { create(:user) }
- context 'users and members' do
+ context 'GET users' do
let!(:user2) { create(:user) }
let!(:non_member) { create(:user) }
@@ -144,6 +144,15 @@ describe AutocompleteController do
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq 0 }
end
+
+ describe 'GET #users with todo filter' do
+ it 'gives an array of users' do
+ get :users, todo_filter: true
+
+ expect(response.status).to eq 200
+ expect(body).to be_kind_of(Array)
+ end
+ end
end
context 'author of issuable included' do
@@ -180,7 +189,7 @@ describe AutocompleteController do
end
end
- context 'projects' do
+ context 'GET projects' do
let(:authorized_project) { create(:project) }
let(:authorized_search_project) { create(:project, name: 'rugged') }
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index cffed987f6b..d3489324a9c 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -8,26 +8,32 @@ describe HelpController do
end
describe 'GET #index' do
- context 'when url prefixed without /help/' do
- it 'has correct url prefix' do
- stub_readme("[API](api/README.md)")
+ context 'with absolute url' do
+ it 'keeps the URL absolute' do
+ stub_readme("[API](/api/README.md)")
+
get :index
- expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
+
+ expect(assigns[:help_index]).to eq '[API](/api/README.md)'
end
end
- context 'when url prefixed with help' do
- it 'will be an absolute path' do
- stub_readme("[API](helpful_hints/README.md)")
+ context 'with relative url' do
+ it 'prefixes it with /help/' do
+ stub_readme("[API](api/README.md)")
+
get :index
- expect(assigns[:help_index]).to eq '[API](/help/helpful_hints/README.md)'
+
+ expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
context 'when url is an external link' do
- it 'will not be changed' do
+ it 'does not change it' do
stub_readme("[external](https://some.external.link)")
+
get :index
+
expect(assigns[:help_index]).to eq '[external](https://some.external.link)'
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 90419368f22..dbe5ddccbcf 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -55,6 +55,30 @@ describe Projects::IssuesController do
end
describe 'GET #new' do
+ context 'internal issue tracker' do
+ before do
+ sign_in(user)
+ project.team << [user, :developer]
+ end
+
+ it 'builds a new issue' do
+ get :new, namespace_id: project.namespace.path, project_id: project
+
+ expect(assigns(:issue)).to be_a_new(Issue)
+ end
+
+ it 'fills in an issue for a merge request' do
+ project_with_repository = create(:project)
+ project_with_repository.team << [user, :developer]
+ mr = create(:merge_request_with_diff_notes, source_project: project_with_repository)
+
+ get :new, namespace_id: project_with_repository.namespace.path, project_id: project_with_repository, merge_request_for_resolving_discussions: mr.iid
+
+ expect(assigns(:issue).title).not_to be_empty
+ expect(assigns(:issue).description).not_to be_empty
+ end
+ end
+
context 'external issue tracker' do
it 'redirects to the external issue tracker' do
external = double(new_issue_path: 'https://example.com/issues/new')
@@ -272,6 +296,42 @@ describe Projects::IssuesController do
end
describe 'POST #create' do
+ context 'resolving discussions in MergeRequest' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+
+ before do
+ project.team << [user, :master]
+ sign_in user
+ end
+
+ let(:merge_request_params) do
+ { merge_request_for_resolving_discussions: merge_request.iid }
+ end
+
+ def post_issue(issue_params)
+ post :create, namespace_id: project.namespace.to_param, project_id: project.to_param, issue: issue_params, merge_request_for_resolving_discussions: merge_request.iid
+ end
+
+ it 'creates an issue for the project' do
+ expect { post_issue({ title: 'Hello' }) }.to change { project.issues.reload.size }.by(1)
+ end
+
+ it "doesn't overwrite given params" do
+ post_issue(description: 'Manually entered description')
+
+ expect(assigns(:issue).description).to eq('Manually entered description')
+ end
+
+ it 'resolves the discussion in the merge_request' do
+ post_issue(title: 'Hello')
+ discussion.first_note.reload
+
+ expect(discussion.resolved?).to eq(true)
+ end
+ end
+
context 'Akismet is enabled' do
before do
allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 1d0750d1719..9e0b80205d8 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -292,7 +292,9 @@ describe Projects::MergeRequestsController do
it 'sets the MR to merge when the build succeeds' do
service = double(:merge_when_build_succeeds_service)
- expect(MergeRequests::MergeWhenBuildSucceedsService).to receive(:new).with(project, anything, anything).and_return(service)
+ expect(MergeRequests::MergeWhenPipelineSucceedsService)
+ .to receive(:new).with(project, anything, anything)
+ .and_return(service)
expect(service).to receive(:execute).with(merge_request)
merge_when_build_succeeds
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index ac2a1ba5dff..1735791f644 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -7,26 +7,30 @@ FactoryGirl.define do
project factory: :empty_project
factory :ci_pipeline_without_jobs do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
+ after(:build) do |pipeline|
+ allow(pipeline).to receive(:ci_yaml_file) { YAML.dump({}) }
end
end
factory :ci_pipeline_with_one_job do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
- end
- end
-
- factory :ci_pipeline_with_two_job do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
+ after(:build) do |pipeline|
+ allow(pipeline).to receive(:ci_yaml_file) do
+ YAML.dump({ rspec: { script: "ls" } })
+ end
end
end
factory :ci_pipeline do
- after(:build) do |commit|
- allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
+ transient { config nil }
+
+ after(:build) do |pipeline, evaluator|
+ allow(pipeline).to receive(:ci_yaml_file) do
+ if evaluator.config
+ YAML.dump(evaluator.config)
+ else
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+ end
end
end
end
diff --git a/spec/features/admin/admin_browses_logs_spec.rb b/spec/features/admin/admin_browses_logs_spec.rb
new file mode 100644
index 00000000000..d880f3f07db
--- /dev/null
+++ b/spec/features/admin/admin_browses_logs_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe 'Admin browses logs' do
+ before do
+ login_as :admin
+ end
+
+ it 'shows available log files' do
+ visit admin_logs_path
+
+ expect(page).to have_content 'test.log'
+ expect(page).to have_content 'githost.log'
+ expect(page).to have_content 'application.log'
+ end
+end
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index b3ce72b1452..f246997d5a2 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -26,16 +26,17 @@ describe "Admin::Hooks", feature: true do
end
describe "New Hook" do
- before do
- @url = FFaker::Internet.uri("http")
+ let(:url) { FFaker::Internet.uri('http') }
+
+ it 'adds new hook' do
visit admin_hooks_path
- fill_in "hook_url", with: @url
- expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1)
- end
+ fill_in 'hook_url', with: url
+ check 'Enable SSL verification'
- it "opens new hook popup" do
+ expect { click_button 'Add System Hook' }.to change(SystemHook, :count).by(1)
+ expect(page).to have_content 'SSL Verification: enabled'
expect(current_path).to eq(admin_hooks_path)
- expect(page).to have_content(@url)
+ expect(page).to have_content(url)
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index cb3191dfdde..e31325ce47b 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -225,4 +225,32 @@ describe "Admin::Users", feature: true do
end
end
end
+
+ describe "GET /admin/users/:id/projects" do
+ before do
+ @group = create(:group)
+ @project = create(:project, group: @group)
+ @simple_user = create(:user)
+ @group.add_developer(@simple_user)
+
+ visit projects_admin_user_path(@simple_user)
+ end
+
+ it "lists group projects" do
+ within(:css, '.append-bottom-default + .panel') do
+ expect(page).to have_content 'Group projects'
+ expect(page).to have_link @group.name, admin_group_path(@group)
+ end
+ end
+
+ it 'allows navigation to the group details' do
+ within(:css, '.append-bottom-default + .panel') do
+ click_link @group.name
+ end
+ within(:css, 'h3.page-title') do
+ expect(page).to have_content "Group: #{@group.name}"
+ end
+ expect(page).to have_content @project.name
+ end
+ end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index f160052a844..c16aafa1470 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -304,8 +304,8 @@ describe 'Issue Boards', feature: true, js: true do
page.within('.subscription') do
click_button 'Subscribe'
-
- expect(page).to have_content("You're receiving notifications because you're subscribed to this thread.")
+ wait_for_ajax
+ expect(page).to have_content("Unsubscribe")
end
end
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 44646ffc602..23a504ff965 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -177,4 +177,23 @@ describe 'Commits' do
end
end
end
+
+ context 'viewing commits for a branch' do
+ let(:branch_name) { 'master' }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ login_with(user)
+ visit namespace_project_commits_path(project.namespace, project, branch_name)
+ end
+
+ it 'includes the committed_date for each commit' do
+ commits = project.repository.commits(branch_name)
+
+ commits.each do |commit|
+ expect(page).to have_content("committed #{commit.committed_date}")
+ end
+ end
+ end
end
diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb
new file mode 100644
index 00000000000..69281cecb7b
--- /dev/null
+++ b/spec/features/groups/labels/edit_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+feature 'Edit group label', feature: true do
+ given(:user) { create(:user) }
+ given(:group) { create(:group) }
+ given(:label) { create(:group_label, group: group) }
+
+ background do
+ group.add_owner(user)
+ login_as(user)
+ visit edit_group_label_path(group, label)
+ end
+
+ scenario 'update label with new title' do
+ fill_in 'label_title', with: 'new label name'
+ click_button 'Save changes'
+
+ expect(current_path).to eq(root_path)
+ expect(label.reload.title).to eq('new label name')
+ end
+end
diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb
index a2791b57544..30b80aa82b0 100644
--- a/spec/features/groups/merge_requests_spec.rb
+++ b/spec/features/groups/merge_requests_spec.rb
@@ -2,7 +2,35 @@ require 'spec_helper'
feature 'Group merge requests page', feature: true do
let(:path) { merge_requests_group_path(group) }
- let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: "this is my created issuable")}
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: 'this is my created issuable') }
include_examples 'project features apply to issuables', MergeRequest
+
+ context 'archived issuable' do
+ let(:project_archived) { create(:project, group: group, merge_requests_access_level: ProjectFeature::ENABLED, archived: true) }
+ let(:issuable_archived) { create(:merge_request, source_project: project_archived, target_project: project_archived, title: 'issuable of an archived project') }
+ let(:access_level) { ProjectFeature::ENABLED }
+ let(:user) { user_in_group }
+
+ before do
+ issuable_archived
+ visit path
+ end
+
+ it 'hides archived merge requests' do
+ expect(page).to have_content(issuable.title)
+ expect(page).not_to have_content(issuable_archived.title)
+ end
+
+ it 'ignores archived merge request count badges in navbar' do
+ expect( page.find('[title="Merge Requests"] span.badge.count').text).to eq("1")
+ end
+
+ it 'ignores archived merge request count badges in state-filters' do
+ expect(page.find('#state-opened span.badge').text).to eq("1")
+ expect(page.find('#state-merged span.badge').text).to eq("0")
+ expect(page.find('#state-closed span.badge').text).to eq("0")
+ expect(page.find('#state-all span.badge').text).to eq("1")
+ end
+ end
end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 73d03837144..4319d6db0d2 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -12,9 +12,9 @@ describe 'Help Pages', feature: true do
end
describe 'Get the main help page' do
- shared_examples_for 'help page' do
+ shared_examples_for 'help page' do |prefix: ''|
it 'prefixes links correctly' do
- expect(page).to have_selector('div.documentation-index > ul a[href="/help/api/README.md"]')
+ expect(page).to have_selector(%(div.documentation-index > ul a[href="#{prefix}/help/api/README.md"]))
end
end
@@ -33,5 +33,14 @@ describe 'Help Pages', feature: true do
it_behaves_like 'help page'
end
+
+ context 'with a relative installation' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab')
+ visit help_path
+ end
+
+ it_behaves_like 'help page', prefix: '/gitlab'
+ end
end
end
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
new file mode 100644
index 00000000000..762cab0c0e1
--- /dev/null
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -0,0 +1,76 @@
+require 'rails_helper'
+
+feature 'Resolving all open discussions in a merge request from an issue', feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first }
+
+ before do
+ project.team << [user, :master]
+ login_as user
+ end
+
+ context 'with the internal tracker disabled' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'does not show a link to create a new issue' do
+ expect(page).not_to have_link 'open an issue to resolve them later'
+ end
+ end
+
+ context 'merge request has discussions that need to be resolved' do
+ before do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'shows a warning that the merge request contains unresolved discussions' do
+ expect(page).to have_content 'This merge request has unresolved discussions'
+ end
+
+ it 'has a link to resolve all discussions by creating an issue' do
+ page.within '.mr-widget-body' do
+ expect(page).to have_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_for_resolving_discussions: merge_request.iid)
+ end
+ end
+
+ context 'creating an issue for discussions' do
+ before do
+ page.click_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_for_resolving_discussions: merge_request.iid)
+ end
+
+ it 'shows an issue with the title filled in' do
+ title_field = page.find_field('issue[title]')
+
+ expect(title_field.value).to include(merge_request.title)
+ end
+
+ it 'has a mention of the discussion in the description' do
+ description_field = page.find_field('issue[description]')
+
+ expect(description_field.value).to include(discussion.first_note.note)
+ end
+
+ it 'has a hidden field for the merge request' do
+ merge_request_field = find('#merge_request_for_resolving_discussions', visible: false)
+
+ expect(merge_request_field.value).to eq(merge_request.iid.to_s)
+ end
+
+ it 'can create a new issue for the project' do
+ expect { click_button 'Submit issue' }.to change { project.issues.reload.size }.by(1)
+ end
+
+ it 'resolves the discussion in the merge request' do
+ click_button 'Submit issue'
+
+ discussion.first_note.reload
+
+ expect(discussion.resolved?).to eq(true)
+ end
+ end
+ end
+end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index c9bec05a9da..f89b4db9e62 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -28,7 +28,7 @@ feature 'issue move to another project' do
let(:new_project) { create(:project) }
let(:new_project_search) { create(:project) }
let(:text) { "Text with #{mr.to_reference}" }
- let(:cross_reference) { old_project.to_reference }
+ let(:cross_reference) { old_project.to_reference(new_project) }
background do
old_project.team << [user, :reporter]
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index d258ff52bbb..5bc4ab2dfe5 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -20,7 +20,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
within find('.files-wrapper .diff-file', text: 'files/ruby/regex.rb') do
all('button', text: 'Use ours').each do |button|
- button.click
+ button.trigger('click')
end
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 702869b6e8b..f1b68a39343 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -40,7 +40,7 @@ feature 'Create New Merge Request', feature: true, js: true do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_project_id: private_project.id })
- expect(page).not_to have_content private_project.to_reference
+ expect(page).not_to have_content private_project.path_with_namespace
end
end
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 778b3a90cf3..d5c9ed8a3b7 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -1,5 +1,8 @@
require 'spec_helper'
+# This test serves as a regression test for a bug that caused an error
+# message to be shown by JavaScript when the source branch was deleted.
+# Please do not remove "js: true".
describe 'Deleted source branch', feature: true, js: true do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 9ad225e3a1b..aa24a905001 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Merge When Build Succeeds', feature: true, js: true do
+feature 'Merge When Pipeline Succeeds', :feature, :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
@@ -18,7 +18,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
before { project.team << [user, :master] }
- context 'when there is active build for merge request' do
+ context 'when there is active pipeline for merge request' do
background do
create(:ci_build, pipeline: pipeline)
end
@@ -28,28 +28,28 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
visit_merge_request(merge_request)
end
- it 'displays the Merge When Build Succeeds button' do
- expect(page).to have_button "Merge When Build Succeeds"
+ it 'displays the Merge When Pipeline Succeeds button' do
+ expect(page).to have_button "Merge When Pipeline Succeeds"
end
- context "Merge When Build succeeds enabled" do
+ context "Merge When Pipeline Succeeds enabled" do
before do
- click_button "Merge When Build Succeeds"
+ click_button "Merge When Pipeline Succeeds"
end
- it 'activates Merge When Build Succeeds feature' do
+ it 'activates Merge When Pipeline Succeeds feature' do
expect(page).to have_link "Cancel Automatic Merge"
- expect(page).to have_content "Set by #{user.name} to be merged automatically when the build succeeds."
+ expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds."
expect(page).to have_content "The source branch will not be removed."
visit_merge_request(merge_request) # Needed to refresh the page
- expect(page).to have_content /enabled an automatic merge when the build for \h{8} succeeds/i
+ expect(page).to have_content /enabled an automatic merge when the pipeline for \h{8} succeeds/i
end
end
end
- context 'when merge when build succeeds is enabled' do
+ context 'when merge when pipeline succeeds is enabled' do
let(:merge_request) do
create(:merge_request_with_diffs, :simple, source_project: project,
author: user,
@@ -70,7 +70,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
it 'allows to cancel the automatic merge' do
click_link "Cancel Automatic Merge"
- expect(page).to have_button "Merge When Build Succeeds"
+ expect(page).to have_button "Merge When Pipeline Succeeds"
visit_merge_request(merge_request) # refresh the page
expect(page).to have_content "canceled the automatic merge"
@@ -83,7 +83,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
expect(page).to have_content "The source branch will be removed"
end
- context 'when build succeeds' do
+ context 'when pipeline succeeds' do
background { build.success }
it 'merges merge request' do
@@ -95,10 +95,11 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
end
end
- context 'when build is not active' do
- it "does not allow to enable merge when build succeeds" do
+ context 'when pipeline is not active' do
+ it "does not allow to enable merge when pipeline succeeds" do
visit_merge_request(merge_request)
- expect(page).not_to have_link "Merge When Build Succeeds"
+
+ expect(page).not_to have_link 'Merge When Pipeline Succeeds'
end
end
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index 1ec3103feef..7e2907cd26f 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -38,7 +38,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
it 'does not allow to merge immediately' do
visit_merge_request(merge_request)
- expect(page).to have_button 'Merge When Build Succeeds'
+ expect(page).to have_button 'Merge When Pipeline Succeeds'
expect(page).not_to have_button 'Select Merge Moment'
end
end
@@ -97,7 +97,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
it 'allows MR to be merged immediately', js: true do
visit_merge_request(merge_request)
- expect(page).to have_button 'Merge When Build Succeeds'
+ expect(page).to have_button 'Merge When Pipeline Succeeds'
click_button 'Select Merge Moment'
expect(page).to have_content 'Merge Immediately'
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
new file mode 100644
index 00000000000..b6134540273
--- /dev/null
+++ b/spec/features/merge_requests/target_branch_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe 'Target branch', feature: true do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+
+ def path_to_merge_request
+ namespace_project_merge_request_path(
+ project.namespace,
+ project, merge_request
+ )
+ end
+
+ before do
+ login_as user
+ project.team << [user, :master]
+ end
+
+ it 'shows link to target branch' do
+ visit path_to_merge_request
+ expect(page).to have_link('feature', href: namespace_project_commits_path(project.namespace, project, merge_request.target_branch))
+ end
+
+ context 'when branch was deleted' do
+ before do
+ DeleteBranchService.new(project, user).execute('feature')
+ visit path_to_merge_request
+ end
+
+ it 'shows a message about missing target branch' do
+ expect(page).to have_content(
+ 'Target branch feature does not exist'
+ )
+ end
+
+ it 'does not show link to target branch' do
+ expect(page).not_to have_link('feature')
+ end
+ end
+end
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index d14a1158b67..a6b841c0210 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -73,7 +73,7 @@ describe 'Profile > Preferences', feature: true do
expect(page.current_path).to eq starred_dashboard_projects_path
end
- click_link 'Your Projects'
+ click_link 'Your projects'
expect(page).not_to have_content("You don't have starred projects yet")
expect(page.current_path).to eq dashboard_projects_path
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index c22441f8929..8120a51c515 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe "Guest navigation menu" do
- let(:project) { create :empty_project, :private }
- let(:guest) { create :user }
+ let(:project) { create(:empty_project, :private, public_builds: false) }
+ let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
new file mode 100644
index 00000000000..7d0065ee2c4
--- /dev/null
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+feature 'Projects members', feature: true do
+ let(:user) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:group) { create(:group, :public, :access_requestable) }
+ let(:project) { create(:empty_project, :public, :access_requestable, creator: user, group: group) }
+ let(:project_invitee) { create(:project_member, project: project, invite_token: '123', invite_email: 'test1@abc.com', user: nil) }
+ let(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) }
+ let(:project_requester) { create(:user) }
+ let(:group_requester) { create(:user) }
+
+ background do
+ project.team << [developer, :developer]
+ group.add_owner(user)
+ login_as(user)
+ end
+
+ context 'with a group invitee' do
+ before do
+ group_invitee
+ visit namespace_project_project_members_path(project.namespace, project)
+ end
+
+ scenario 'does not appear in the project members page' do
+ page.within first('.content-list') do
+ expect(page).not_to have_content('test2@abc.com')
+ end
+ end
+ end
+
+ context 'with a group and a project invitee' do
+ before do
+ group_invitee
+ project_invitee
+ visit namespace_project_project_members_path(project.namespace, project)
+ end
+
+ scenario 'shows the project invitee, the project developer, and the group owner' do
+ page.within first('.content-list') do
+ expect(page).to have_content('test1@abc.com')
+ expect(page).not_to have_content('test2@abc.com')
+
+ # Project developer
+ expect(page).to have_content(developer.name)
+
+ # Group owner
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+ end
+
+ context 'with a group requester' do
+ before do
+ group.request_access(group_requester)
+ visit namespace_project_project_members_path(project.namespace, project)
+ end
+
+ scenario 'does not appear in the project members page' do
+ page.within first('.content-list') do
+ expect(page).not_to have_content(group_requester.name)
+ end
+ end
+ end
+
+ context 'with a group and a project requesters' do
+ before do
+ group.request_access(group_requester)
+ project.request_access(project_requester)
+ visit namespace_project_project_members_path(project.namespace, project)
+ end
+
+ scenario 'shows the project requester, the project developer, and the group owner' do
+ page.within first('.content-list') do
+ expect(page).to have_content(project_requester.name)
+ expect(page).not_to have_content(group_requester.name)
+ end
+
+ page.within all('.content-list').last do
+ # Project developer
+ expect(page).to have_content(developer.name)
+
+ # Group owner
+ expect(page).to have_content(user.name)
+ expect(page).to have_content(group.name)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
new file mode 100644
index 00000000000..3350a3aeefc
--- /dev/null
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -0,0 +1,154 @@
+require 'spec_helper'
+
+describe "Pipelines", feature: true, js: true do
+ include GitlabRoutingHelper
+
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ login_as(user)
+ project.team << [user, :developer]
+ end
+
+ describe 'GET /:project/pipelines/:id' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+
+ before do
+ @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build')
+ @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test')
+ @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy')
+ @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual build')
+ @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external')
+ end
+
+ before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
+
+ it 'shows the pipeline graph' do
+ expect(page).to have_selector('.pipeline-visualization')
+ expect(page).to have_content('Build')
+ expect(page).to have_content('Test')
+ expect(page).to have_content('Deploy')
+ expect(page).to have_content('Retry failed')
+ expect(page).to have_content('Cancel running')
+ end
+
+ it 'shows Pipeline tab pane as active' do
+ expect(page).to have_css('#js-tab-pipeline.active')
+ end
+
+ context 'page tabs' do
+ it 'shows Pipeline and Builds tabs with link' do
+ expect(page).to have_link('Pipeline')
+ expect(page).to have_link('Builds')
+ end
+
+ it 'shows counter in Builds tab' do
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.statuses.count.to_s)
+ end
+
+ it 'shows Pipeline tab as active' do
+ expect(page).to have_css('.js-pipeline-tab-link.active')
+ end
+ end
+
+ context 'retrying builds' do
+ it { expect(page).not_to have_content('retried') }
+
+ context 'when retrying' do
+ before { click_on 'Retry failed' }
+
+ it { expect(page).not_to have_content('Retry failed') }
+ end
+ end
+
+ context 'canceling builds' do
+ it { expect(page).not_to have_selector('.ci-canceled') }
+
+ context 'when canceling' do
+ before { click_on 'Cancel running' }
+
+ it { expect(page).not_to have_content('Cancel running') }
+ end
+ end
+ end
+
+ describe 'GET /:project/pipelines/:id/builds' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+
+ before do
+ @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build')
+ @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test')
+ @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy')
+ @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual build')
+ @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external')
+ end
+
+ before { visit builds_namespace_project_pipeline_path(project.namespace, project, pipeline)}
+
+ it 'shows a list of builds' do
+ expect(page).to have_content('Test')
+ expect(page).to have_content(@success.id)
+ expect(page).to have_content('Deploy')
+ expect(page).to have_content(@failed.id)
+ expect(page).to have_content(@running.id)
+ expect(page).to have_content(@external.id)
+ expect(page).to have_content('Retry failed')
+ expect(page).to have_content('Cancel running')
+ expect(page).to have_link('Play')
+ end
+
+ it 'shows Builds tab pane as active' do
+ expect(page).to have_css('#js-tab-builds.active')
+ end
+
+ context 'page tabs' do
+ it 'shows Pipeline and Builds tabs with link' do
+ expect(page).to have_link('Pipeline')
+ expect(page).to have_link('Builds')
+ end
+
+ it 'shows counter in Builds tab' do
+ expect(page.find('.js-builds-counter').text).to eq(pipeline.statuses.count.to_s)
+ end
+
+ it 'shows Builds tab as active' do
+ expect(page).to have_css('li.js-builds-tab-link.active')
+ end
+ end
+
+ context 'retrying builds' do
+ it { expect(page).not_to have_content('retried') }
+
+ context 'when retrying' do
+ before { click_on 'Retry failed' }
+
+ it { expect(page).not_to have_content('Retry failed') }
+ it { expect(page).to have_selector('.retried') }
+ end
+ end
+
+ context 'canceling builds' do
+ it { expect(page).not_to have_selector('.ci-canceled') }
+
+ context 'when canceling' do
+ before { click_on 'Cancel running' }
+
+ it { expect(page).not_to have_content('Cancel running') }
+ it { expect(page).to have_selector('.ci-canceled') }
+ end
+ end
+
+ context 'playing manual build' do
+ before do
+ within '.pipeline-holder' do
+ click_link('Play')
+ end
+ end
+
+ it { expect(@manual.reload).to be_pending }
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 10e5466fc85..f3731698a18 100644
--- a/spec/features/projects/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -152,65 +152,6 @@ describe "Pipelines" do
end
end
- describe 'GET /:project/pipelines/:id' do
- let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
-
- before do
- @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build')
- @failed = create(:ci_build, :failed, pipeline: pipeline, stage: 'test', name: 'test', commands: 'test')
- @running = create(:ci_build, :running, pipeline: pipeline, stage: 'deploy', name: 'deploy')
- @manual = create(:ci_build, :manual, pipeline: pipeline, stage: 'deploy', name: 'manual build')
- @external = create(:generic_commit_status, status: 'success', pipeline: pipeline, name: 'jenkins', stage: 'external')
- end
-
- before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
-
- it 'shows a list of builds' do
- expect(page).to have_content('Test')
- expect(page).to have_content(@success.id)
- expect(page).to have_content('Deploy')
- expect(page).to have_content(@failed.id)
- expect(page).to have_content(@running.id)
- expect(page).to have_content(@external.id)
- expect(page).to have_content('Retry failed')
- expect(page).to have_content('Cancel running')
- expect(page).to have_link('Play')
- end
-
- context 'retrying builds' do
- it { expect(page).not_to have_content('retried') }
-
- context 'when retrying' do
- before { click_on 'Retry failed' }
-
- it { expect(page).not_to have_content('Retry failed') }
- it { expect(page).to have_selector('.retried') }
- end
- end
-
- context 'canceling builds' do
- it { expect(page).not_to have_selector('.ci-canceled') }
-
- context 'when canceling' do
- before { click_on 'Cancel running' }
-
- it { expect(page).not_to have_content('Cancel running') }
- it { expect(page).to have_selector('.ci-canceled') }
- end
- end
-
- context 'playing manual build' do
- before do
- within '.pipeline-holder' do
- click_link('Play')
- end
- end
-
- it { expect(@manual.reload).to be_pending }
- end
- end
-
describe 'POST /:project/pipelines' do
let(:project) { create(:project) }
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 7afd83b7250..fff8b9f3447 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -20,7 +20,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Home')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
@@ -41,7 +41,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Foo')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
@@ -55,7 +55,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Spaces in the name')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
@@ -69,7 +69,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Hyphens in the name')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
@@ -85,7 +85,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Home')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
@@ -105,7 +105,7 @@ feature 'Projects > Wiki > User creates wiki page', feature: true do
click_button 'Create page'
expect(page).to have_content('Foo')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index ef82d2375dd..f842d14fa96 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -22,7 +22,7 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
click_button 'Save changes'
expect(page).to have_content('Home')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
@@ -37,7 +37,7 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
click_button 'Save changes'
expect(page).to have_content('Home')
- expect(page).to have_content("last edited by #{user.name}")
+ expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
end
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
index 35fcef7a712..87cce32d6c6 100644
--- a/spec/features/security/group/internal_access_spec.rb
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -3,25 +3,12 @@ require 'rails_helper'
describe 'Internal Group access', feature: true do
include AccessMatchers
- let(:group) { create(:group, :internal) }
+ let(:group) { create(:group, :internal) }
let(:project) { create(:project, :internal, group: group) }
-
- let(:owner) { create(:user) }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- let(:project_guest) { create(:user) }
-
- before do
- group.add_owner(owner)
- group.add_master(master)
- group.add_developer(developer)
- group.add_reporter(reporter)
- group.add_guest(guest)
-
- project.team << [project_guest, :guest]
+ let(:project_guest) do
+ create(:user) do |user|
+ project.add_guest(user)
+ end
end
describe "Group should be internal" do
@@ -34,75 +21,75 @@ describe 'Internal Group access', feature: true do
describe 'GET /groups/:path' do
subject { group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_denied_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 project_guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :visitor }
- it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:developer).of(group) }
+ it { is_expected.to be_denied_for(:reporter).of(group) }
+ it { is_expected.to be_denied_for(:guest).of(group) }
+ it { is_expected.to be_denied_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
end
end
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index 75a93342628..1d6b3e77c22 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -3,25 +3,12 @@ require 'rails_helper'
describe 'Private Group access', feature: true do
include AccessMatchers
- let(:group) { create(:group, :private) }
+ let(:group) { create(:group, :private) }
let(:project) { create(:project, :private, group: group) }
-
- let(:owner) { create(:user) }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- let(:project_guest) { create(:user) }
-
- before do
- group.add_owner(owner)
- group.add_master(master)
- group.add_developer(developer)
- group.add_reporter(reporter)
- group.add_guest(guest)
-
- project.team << [project_guest, :guest]
+ let(:project_guest) do
+ create(:user) do |user|
+ project.add_guest(user)
+ end
end
describe "Group should be private" do
@@ -34,75 +21,75 @@ describe 'Private Group access', feature: true do
describe 'GET /groups/:path' do
subject { group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_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(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_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(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_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(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_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(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_denied_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 project_guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :visitor }
- it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:developer).of(group) }
+ it { is_expected.to be_denied_for(:reporter).of(group) }
+ it { is_expected.to be_denied_for(:guest).of(group) }
+ it { is_expected.to be_denied_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
end
end
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
index 6c5ee93970b..d7d76177269 100644
--- a/spec/features/security/group/public_access_spec.rb
+++ b/spec/features/security/group/public_access_spec.rb
@@ -3,25 +3,12 @@ require 'rails_helper'
describe 'Public Group access', feature: true do
include AccessMatchers
- let(:group) { create(:group, :public) }
+ let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, group: group) }
-
- let(:owner) { create(:user) }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
-
- let(:project_guest) { create(:user) }
-
- before do
- group.add_owner(owner)
- group.add_master(master)
- group.add_developer(developer)
- group.add_reporter(reporter)
- group.add_guest(guest)
-
- project.team << [project_guest, :guest]
+ let(:project_guest) do
+ create(:user) do |user|
+ project.add_guest(user)
+ end
end
describe "Group should be public" do
@@ -34,75 +21,75 @@ describe 'Public Group access', feature: true do
describe 'GET /groups/:path' do
subject { group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for project_guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_allowed_for(:master).of(group) }
+ it { is_expected.to be_allowed_for(:developer).of(group) }
+ it { is_expected.to be_allowed_for(:reporter).of(group) }
+ it { is_expected.to be_allowed_for(:guest).of(group) }
+ it { is_expected.to be_allowed_for(project_guest) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_denied_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 project_guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :visitor }
- it { is_expected.to be_denied_for :external }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(group) }
+ it { is_expected.to be_denied_for(:master).of(group) }
+ it { is_expected.to be_denied_for(:developer).of(group) }
+ it { is_expected.to be_denied_for(:reporter).of(group) }
+ it { is_expected.to be_denied_for(:guest).of(group) }
+ it { is_expected.to be_denied_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:visitor) }
+ it { is_expected.to be_denied_for(:external) }
end
end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index 290ddb4c6dd..f52e23f9433 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe "Private Project Access", feature: true do
include AccessMatchers
- let(:project) { create(:project, :private) }
+ let(:project) { create(:project, :private, public_builds: false) }
describe "Project should be private" do
describe '#private?' do
@@ -260,6 +260,18 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:external) }
it { is_expected.to be_denied_for(:visitor) }
+
+ context 'when public builds is enabled' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ end
+
+ context 'when public buils are disabled' do
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ end
end
describe "GET /:project_path/pipelines/:id" do
@@ -275,6 +287,18 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:external) }
it { is_expected.to be_denied_for(:visitor) }
+
+ context 'when public builds is enabled' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ end
+
+ context 'when public buils are disabled' do
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ end
end
describe "GET /:project_path/builds" do
@@ -289,6 +313,18 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:external) }
it { is_expected.to be_denied_for(:visitor) }
+
+ context 'when public builds is enabled' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ end
+
+ context 'when public buils are disabled' do
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ end
end
describe "GET /:project_path/builds/:id" do
@@ -305,6 +341,23 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:user) }
it { is_expected.to be_denied_for(:external) }
it { is_expected.to be_denied_for(:visitor) }
+
+ context 'when public builds is enabled' do
+ before do
+ project.update(public_builds: true)
+ end
+
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ end
+
+ context 'when public buils are disabled' do
+ before do
+ project.public_builds = false
+ project.save
+ end
+
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ end
end
describe "GET /:project_path/environments" do
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
index 49deacc5c74..2659b3ee3ec 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -5,76 +5,64 @@ describe "Internal Project Snippets Access", feature: true do
let(:project) { create(:empty_project, :internal) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
- let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) }
- let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
+ let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: project.owner) }
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: project.owner) }
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/:id" do
context "for an internal snippet" do
subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "for a private snippet" do
subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
@@ -82,29 +70,29 @@ describe "Internal Project Snippets Access", feature: true do
context "for an internal snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, internal_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "for a private snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
end
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
index a1bfc076d99..6eb9f163bd5 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -5,73 +5,61 @@ describe "Private Project Snippets Access", feature: true do
let(:project) { create(:empty_project, :private) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
- let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: project.owner) }
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/:id for a private snippet" do
subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/:id/raw for a private snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index 30bcd87ef04..f3329d0bc96 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -5,91 +5,79 @@ describe "Public Project Snippets Access", feature: true do
let(:project) { create(:empty_project, :public) }
- let(:owner) { project.owner }
- let(:master) { create(:user) }
- let(:developer) { create(:user) }
- let(:reporter) { create(:user) }
- let(:guest) { create(:user) }
- let(:public_snippet) { create(:project_snippet, :public, project: project, author: owner) }
- let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) }
- let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) }
-
- before do
- project.team << [master, :master]
- project.team << [developer, :developer]
- project.team << [reporter, :reporter]
- project.team << [guest, :guest]
- end
+ let(:public_snippet) { create(:project_snippet, :public, project: project, author: project.owner) }
+ let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: project.owner) }
+ let(:private_snippet) { create(:project_snippet, :private, project: project, author: project.owner) }
describe "GET /:project_path/snippets" do
subject { namespace_project_snippets_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/snippets/new" do
subject { new_namespace_project_snippet_path(project.namespace, project) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_denied_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/snippets/:id" do
context "for a public snippet" do
subject { namespace_project_snippet_path(project.namespace, project, public_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
context "for an internal snippet" do
subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "for a private snippet" do
subject { namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
@@ -97,43 +85,43 @@ describe "Public Project Snippets Access", feature: true do
context "for a public snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, public_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_allowed_for :external }
- it { is_expected.to be_allowed_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_allowed_for(:external) }
+ it { is_expected.to be_allowed_for(:visitor) }
end
context "for an internal snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, internal_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_allowed_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
context "for a private snippet" do
subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for owner }
- it { is_expected.to be_allowed_for master }
- it { is_expected.to be_allowed_for developer }
- it { is_expected.to be_allowed_for reporter }
- it { is_expected.to be_allowed_for guest }
- it { is_expected.to be_denied_for :user }
- it { is_expected.to be_denied_for :external }
- it { is_expected.to be_denied_for :visitor }
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_allowed_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 40bccb8e50b..7f69e888f32 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -10,6 +10,7 @@ describe IssuesFinder do
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') }
let(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') }
let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) }
+ let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
let!(:label_link) { create(:label_link, label: label, target: issue2) }
before do
@@ -25,7 +26,7 @@ describe IssuesFinder do
describe '#execute' do
let(:search_user) { user }
let(:params) { {} }
- let(:issues) { IssuesFinder.new(search_user, params.merge(scope: scope, state: 'opened')).execute }
+ let(:issues) { IssuesFinder.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
context 'scope: all' do
let(:scope) { 'all' }
@@ -143,6 +144,40 @@ describe IssuesFinder do
end
end
+ context 'filtering by state' do
+ context 'with opened' do
+ let(:params) { { state: 'opened' } }
+
+ it 'returns only opened issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
+ end
+ end
+
+ context 'with closed' do
+ let(:params) { { state: 'closed' } }
+
+ it 'returns only closed issues' do
+ expect(issues).to contain_exactly(closed_issue)
+ end
+ end
+
+ context 'with all' do
+ let(:params) { { state: 'all' } }
+
+ it 'returns all issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue)
+ end
+ end
+
+ context 'with invalid state' do
+ let(:params) { { state: 'invalid_state' } }
+
+ it 'returns all issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, closed_issue)
+ end
+ end
+ end
+
context 'when the user is unauthorized' do
let(:search_user) { nil }
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 9085cc8debf..1724cdba830 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -17,7 +17,7 @@ describe LabelsFinder do
let!(:project_label_4) { create(:label, project: project_4, title: 'Label 4') }
let!(:project_label_5) { create(:label, project: project_5, title: 'Label 5') }
- let!(:group_label_1) { create(:group_label, group: group_1, title: 'Label 1') }
+ let!(:group_label_1) { create(:group_label, group: group_1, title: 'Label 1 (group)') }
let!(:group_label_2) { create(:group_label, group: group_1, title: 'Group Label 2') }
let!(:group_label_3) { create(:group_label, group: group_2, title: 'Group Label 3') }
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 535aabfc18d..88361e27102 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -6,14 +6,17 @@ describe MergeRequestsFinder do
let(:project1) { create(:project) }
let(:project2) { create(:project, forked_from_project: project1) }
+ let(:project3) { create(:project, forked_from_project: project1, archived: true) }
let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) }
let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') }
let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) }
+ let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) }
before do
project1.team << [user, :master]
project2.team << [user, :developer]
+ project3.team << [user, :developer]
project2.team << [user2, :developer]
end
@@ -21,7 +24,7 @@ describe MergeRequestsFinder do
it 'filters by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = MergeRequestsFinder.new(user, params).execute
- expect(merge_requests.size).to eq(2)
+ expect(merge_requests.size).to eq(3)
end
it 'filters by project' do
@@ -29,5 +32,11 @@ describe MergeRequestsFinder do
merge_requests = MergeRequestsFinder.new(user, params).execute
expect(merge_requests.size).to eq(1)
end
+
+ it 'filters by non_archived' do
+ params = { non_archived: true }
+ merge_requests = MergeRequestsFinder.new(user, params).execute
+ expect(merge_requests.size).to eq(3)
+ end
end
end
diff --git a/spec/fixtures/api/schemas/user/login.json b/spec/fixtures/api/schemas/user/login.json
new file mode 100644
index 00000000000..e6c1d9c9d84
--- /dev/null
+++ b/spec/fixtures/api/schemas/user/login.json
@@ -0,0 +1,37 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "username",
+ "email",
+ "name",
+ "state",
+ "avatar_url",
+ "web_url",
+ "created_at",
+ "is_admin",
+ "bio",
+ "location",
+ "skype",
+ "linkedin",
+ "twitter",
+ "website_url",
+ "organization",
+ "last_sign_in_at",
+ "confirmed_at",
+ "theme_id",
+ "color_scheme_id",
+ "projects_limit",
+ "current_sign_in_at",
+ "identities",
+ "can_create_group",
+ "can_create_project",
+ "two_factor_enabled",
+ "external",
+ "private_token"
+ ],
+ "properties": {
+ "$ref": "full.json",
+ "private_token": { "type": "string" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/user/public.json b/spec/fixtures/api/schemas/user/public.json
new file mode 100644
index 00000000000..dbd5d32e89c
--- /dev/null
+++ b/spec/fixtures/api/schemas/user/public.json
@@ -0,0 +1,79 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "username",
+ "email",
+ "name",
+ "state",
+ "avatar_url",
+ "web_url",
+ "created_at",
+ "is_admin",
+ "bio",
+ "location",
+ "skype",
+ "linkedin",
+ "twitter",
+ "website_url",
+ "organization",
+ "last_sign_in_at",
+ "confirmed_at",
+ "theme_id",
+ "color_scheme_id",
+ "projects_limit",
+ "current_sign_in_at",
+ "identities",
+ "can_create_group",
+ "can_create_project",
+ "two_factor_enabled",
+ "external"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "username": { "type": "string" },
+ "email": {
+ "type": "string",
+ "pattern": "^[^@]+@[^@]+$"
+ },
+ "name": { "type": "string" },
+ "state": {
+ "type": "string",
+ "enum": ["active", "blocked"]
+ },
+ "avatar_url": { "type": "string" },
+ "web_url": { "type": "string" },
+ "created_at": { "type": "date" },
+ "is_admin": { "type": "boolean" },
+ "bio": { "type": ["string", "null"] },
+ "location": { "type": ["string", "null"] },
+ "skype": { "type": "string" },
+ "linkedin": { "type": "string" },
+ "twitter": { "type": "string "},
+ "website_url": { "type": "string" },
+ "organization": { "type": ["string", "null"] },
+ "last_sign_in_at": { "type": "date" },
+ "confirmed_at": { "type": ["date", "null"] },
+ "theme_id": { "type": "integer" },
+ "color_scheme_id": { "type": "integer" },
+ "projects_limit": { "type": "integer" },
+ "current_sign_in_at": { "type": "date" },
+ "identities": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "provider": {
+ "type": "string",
+ "enum": ["github", "bitbucket", "google_oauth2"]
+ },
+ "extern_uid": { "type": ["number", "string"] }
+ }
+ }
+ },
+ "can_create_group": { "type": "boolean" },
+ "can_create_project": { "type": "boolean" },
+ "two_factor_enabled": { "type": "boolean" },
+ "external": { "type": "boolean" }
+ }
+}
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index d30daf47543..7cf535fadae 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -7,7 +7,7 @@ describe LabelsHelper do
context 'without subject' do
it "uses the label's project" do
- expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label)).to match %r{<a href="/#{label.project.path_with_namespace}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
@@ -32,7 +32,7 @@ describe LabelsHelper do
['issue', :issue, 'merge_request', :merge_request].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.to_reference}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.path_with_namespace}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
end
diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js
deleted file mode 100644
index 7e38abc608e..00000000000
--- a/spec/javascripts/application_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, padded-blocks, max-len */
-
-/*= require lib/utils/common_utils */
-
-(function() {
- describe('Application', function() {
- return describe('disable buttons', function() {
- fixture.preload('application.html');
- beforeEach(function() {
- return fixture.load('application.html');
- });
- it('should prevent default action for disabled buttons', function() {
- var $button, isClicked;
- gl.utils.preventDisabledButtons();
- isClicked = false;
- $button = $('#test-button');
- expect($button).toExist();
- $button.click(function() {
- return isClicked = true;
- });
- $button.trigger('click');
- return expect(isClicked).toBe(false);
- });
-
- it('should be on the same page if a disabled link clicked', function() {
- var locationBeforeLinkClick, $link;
- locationBeforeLinkClick = window.location.href;
- gl.utils.preventDisabledButtons();
- $link = $('#test-link');
- expect($link).toExist();
- $link.click();
- return expect(window.location.href).toBe(locationBeforeLinkClick);
- });
- });
- });
-
-}).call(this);
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js.es6 b/spec/javascripts/bootstrap_linked_tabs_spec.js.es6
new file mode 100644
index 00000000000..133712debab
--- /dev/null
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js.es6
@@ -0,0 +1,59 @@
+//= require lib/utils/bootstrap_linked_tabs
+
+(() => {
+ describe('Linked Tabs', () => {
+ fixture.preload('linked_tabs');
+
+ beforeEach(() => {
+ fixture.load('linked_tabs');
+ });
+
+ describe('when is initialized', () => {
+ beforeEach(() => {
+ spyOn(window.history, 'replaceState').and.callFake(function () {});
+ });
+
+ it('should activate the tab correspondent to the given action', () => {
+ const linkedTabs = new window.gl.LinkedTabs({ // eslint-disable-line
+ action: 'tab1',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
+ });
+
+ expect(document.querySelector('#tab1').classList).toContain('active');
+ });
+
+ it('should active the default tab action when the action is show', () => {
+ const linkedTabs = new window.gl.LinkedTabs({ // eslint-disable-line
+ action: 'show',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
+ });
+
+ expect(document.querySelector('#tab1').classList).toContain('active');
+ });
+ });
+
+ describe('on click', () => {
+ it('should change the url according to the clicked tab', () => {
+ const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
+
+ const linkedTabs = new window.gl.LinkedTabs({ // eslint-disable-line
+ action: 'show',
+ defaultAction: 'tab1',
+ parentEl: '.linked-tabs',
+ });
+
+ const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a');
+ const newState = secondTab.getAttribute('href') + linkedTabs.currentLocation.search + linkedTabs.currentLocation.hash;
+
+ secondTab.click();
+
+ expect(historySpy).toHaveBeenCalledWith({
+ turbolinks: true,
+ url: newState,
+ }, document.title, newState);
+ });
+ });
+ });
+})();
diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6
index 14e90a9dd1b..5d7c6b2411d 100644
--- a/spec/javascripts/environments/environment_item_spec.js.es6
+++ b/spec/javascripts/environments/environment_item_spec.js.es6
@@ -1,4 +1,5 @@
//= require vue
+//= require timeago
//= require environments/components/environment_item
describe('Environment item', () => {
@@ -109,6 +110,8 @@ describe('Environment item', () => {
name: 'deploy',
build_path: '/root/ci-folders/builds/1279',
retry_path: '/root/ci-folders/builds/1279/retry',
+ created_at: '2016-11-29T18:11:58.430Z',
+ updated_at: '2016-11-29T18:11:58.430Z',
},
manual_actions: [
{
@@ -149,6 +152,17 @@ describe('Environment item', () => {
).toContain('#');
});
+ it('should render last deployment date', () => {
+ const timeagoInstance = new timeago(); // eslint-disable-line
+ const formatedDate = timeagoInstance.format(
+ environment.last_deployment.deployable.created_at,
+ );
+
+ expect(
+ component.$el.querySelector('.environment-created-date-timeago').textContent,
+ ).toContain(formatedDate);
+ });
+
describe('With user information', () => {
it('should render user avatar with link to profile', () => {
expect(
diff --git a/spec/javascripts/extensions/element_spec.js.es6 b/spec/javascripts/extensions/element_spec.js.es6
new file mode 100644
index 00000000000..c5b86d35204
--- /dev/null
+++ b/spec/javascripts/extensions/element_spec.js.es6
@@ -0,0 +1,38 @@
+/*= require extensions/element */
+
+(() => {
+ describe('Element extensions', function () {
+ beforeEach(() => {
+ this.element = document.createElement('ul');
+ });
+
+ describe('matches', () => {
+ it('returns true if element matches the selector', () => {
+ expect(this.element.matches('ul')).toBeTruthy();
+ });
+
+ it("returns false if element doesn't match the selector", () => {
+ expect(this.element.matches('.not-an-element')).toBeFalsy();
+ });
+ });
+
+ describe('closest', () => {
+ beforeEach(() => {
+ this.childElement = document.createElement('li');
+ this.element.appendChild(this.childElement);
+ });
+
+ it('returns the closest parent that matches the selector', () => {
+ expect(this.childElement.closest('ul').toString()).toBe(this.element.toString());
+ });
+
+ it('returns itself if it matches the selector', () => {
+ expect(this.childElement.closest('li').toString()).toBe(this.childElement.toString());
+ });
+
+ it('returns undefined if nothing matches the selector', () => {
+ expect(this.childElement.closest('.no-an-element')).toBeFalsy();
+ });
+ });
+ });
+})();
diff --git a/spec/javascripts/fixtures/application.html.haml b/spec/javascripts/fixtures/application.html.haml
deleted file mode 100644
index 3fc6114407d..00000000000
--- a/spec/javascripts/fixtures/application.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%a#test-link.btn.disabled{:href => "/foo"} Test link
-%button#test-button.btn.disabled Test Button
diff --git a/spec/javascripts/fixtures/comments.html.haml b/spec/javascripts/fixtures/comments.html.haml
deleted file mode 100644
index cc1f8f15c21..00000000000
--- a/spec/javascripts/fixtures/comments.html.haml
+++ /dev/null
@@ -1,21 +0,0 @@
-.flash-container.timeline-content
-.timeline-icon.hidden-xs.hidden-sm
- %a.author_link
- %img
-.timeline-content.timeline-content-form
- %form.new-note.js-quick-submit.common-note-form.gfm-form.js-main-target-form
- .md-area
- .md-header
- .md-write-holder
- .zen-backdrop.div-dropzone-wrapper
- .div-dropzone-wrapper
- .div-dropzone.dz-clickable
- %textarea.note-textarea.js-note-text.js-gfm-input.js-autosize.markdown-area
- .note-form-actions.clearfix
- %input.btn.btn-nr.btn-create.append-right-10.comment-btn.js-comment-button{ type: 'submit' }
- %a.btn.btn-nr.btn-reopen.btn-comment.js-note-target-reopen
- Reopen issue
- %a.btn.btn-nr.btn-close.btn-comment.js-note-target-close
- Close issue
- %a.btn.btn-cancel.js-note-discard
- Discard draft \ No newline at end of file
diff --git a/spec/javascripts/fixtures/issuable.html.haml b/spec/javascripts/fixtures/issuable.html.haml
deleted file mode 100644
index 42ab4aa68b1..00000000000
--- a/spec/javascripts/fixtures/issuable.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%form.js-main-target-form
- %textarea#note_note
diff --git a/spec/javascripts/fixtures/issue_note.html.haml b/spec/javascripts/fixtures/issue_note.html.haml
deleted file mode 100644
index 0aecc7334fd..00000000000
--- a/spec/javascripts/fixtures/issue_note.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-%ul
- %li.note
- .js-task-list-container
- .note-text
- %ul.task-list
- %li.task-list-item
- %input.task-list-item-checkbox{type: 'checkbox'}
- Task List Item
- .note-edit-form
- %form
- %textarea.js-task-list-field
- \- [ ] Task List Item
diff --git a/spec/javascripts/fixtures/issues.rb b/spec/javascripts/fixtures/issues.rb
index c10784fe5ae..06f708f9e15 100644
--- a/spec/javascripts/fixtures/issues.rb
+++ b/spec/javascripts/fixtures/issues.rb
@@ -26,8 +26,13 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller
end
it 'issues/issue-with-task-list.html.raw' do |example|
+ issue = create(:issue, project: project, description: '- [ ] Task List Item')
+ render_issue(example.description, issue)
+ end
+
+ it 'issues/issue_with_comment.html.raw' do |example|
issue = create(:issue, project: project)
- issue.update(description: '- [ ] Task List Item')
+ create(:note, project: project, noteable: issue, note: '- [ ] Task List Item').save
render_issue(example.description, issue)
end
diff --git a/spec/javascripts/fixtures/linked_tabs.html.haml b/spec/javascripts/fixtures/linked_tabs.html.haml
new file mode 100644
index 00000000000..93c0cf97ff0
--- /dev/null
+++ b/spec/javascripts/fixtures/linked_tabs.html.haml
@@ -0,0 +1,13 @@
+%ul.nav.nav-tabs.linked-tabs
+ %li
+ %a{ href: 'foo/bar/1', data: { target: 'div#tab1', action: 'tab1', toggle: 'tab' } }
+ Tab 1
+ %li
+ %a{ href: 'foo/bar/1/context', data: { target: 'div#tab2', action: 'tab2', toggle: 'tab' } }
+ Tab 2
+
+.tab-content
+ #tab1.tab-pane
+ Tab 1 Content
+ #tab2.tab-pane
+ Tab 2 Content
diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml
deleted file mode 100644
index d259b58f235..00000000000
--- a/spec/javascripts/fixtures/right_sidebar.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-%div
- %div.page-gutter.page-with-sidebar
-
- %aside.right-sidebar
- %div.block.issuable-sidebar-header
- %a.gutter-toggle.pull-right.js-sidebar-toggle
- %i.fa.fa-angle-double-left
- %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: "1", issuable_type: "issue", url: "/todos" }}
- %span.js-issuable-todo-text
- Add todo
- %i.fa.fa-spin.fa-spinner.js-issuable-todo-loading.hidden
-
- %form.issuable-context-form
- %div.block.labels
- %div.sidebar-collapsed-icon
- %i.fa.fa-tags
- %span 1
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js.es6 b/spec/javascripts/lib/utils/common_utils_spec.js.es6
new file mode 100644
index 00000000000..ef75f600898
--- /dev/null
+++ b/spec/javascripts/lib/utils/common_utils_spec.js.es6
@@ -0,0 +1,32 @@
+//= require lib/utils/common_utils
+
+(() => {
+ describe('common_utils', () => {
+ describe('gl.utils.parseUrl', () => {
+ it('returns an anchor tag with url', () => {
+ expect(gl.utils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url');
+ });
+ it('url is escaped', () => {
+ // IE11 will return a relative pathname while other browsers will return a full pathname.
+ // parseUrl uses an anchor element for parsing an url. With relative urls, the anchor
+ // element will create an absolute url relative to the current execution context.
+ // The JavaScript test suite is executed at '/teaspoon' which will lead to an absolute
+ // url starting with '/teaspoon'.
+ expect(gl.utils.parseUrl('" test="asf"').pathname).toEqual('/teaspoon/%22%20test=%22asf%22');
+ });
+ });
+ describe('gl.utils.parseUrlPathname', () => {
+ beforeEach(() => {
+ spyOn(gl.utils, 'parseUrl').and.callFake(url => ({
+ pathname: url,
+ }));
+ });
+ it('returns an absolute url when given an absolute url', () => {
+ expect(gl.utils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url');
+ });
+ it('returns an absolute url when given a relative url', () => {
+ expect(gl.utils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url');
+ });
+ });
+ });
+})();
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 971222c44e1..130d391bfab 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -1,108 +1,122 @@
-/* eslint-disable space-before-function-paren, no-var, comma-dangle, dot-notation, quotes, no-undef, no-return-assign, no-underscore-dangle, camelcase, padded-blocks, max-len */
+/* eslint-disable no-var, comma-dangle, object-shorthand */
/*= require merge_request_tabs */
//= require breakpoints
+//= require lib/utils/common_utils
+//= require jquery.scrollTo
-(function() {
- describe('MergeRequestTabs', function() {
- var stubLocation;
- stubLocation = function(stubs) {
- var defaults;
- defaults = {
+(function () {
+ describe('MergeRequestTabs', function () {
+ var stubLocation = {};
+ var setLocation = function (stubs) {
+ var defaults = {
pathname: '',
search: '',
hash: ''
};
- return $.extend(defaults, stubs);
+ $.extend(stubLocation, defaults, stubs || {});
};
fixture.preload('merge_request_tabs.html');
- beforeEach(function() {
- this["class"] = new MergeRequestTabs();
- return this.spies = {
- ajax: spyOn($, 'ajax').and.callFake(function() {}),
- history: spyOn(history, 'replaceState').and.callFake(function() {})
+
+ beforeEach(function () {
+ this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
+ setLocation();
+
+ this.spies = {
+ history: spyOn(window.history, 'replaceState').and.callFake(function () {})
};
});
- describe('#activateTab', function() {
- beforeEach(function() {
+
+ describe('#activateTab', function () {
+ beforeEach(function () {
+ spyOn($, 'ajax').and.callFake(function () {});
fixture.load('merge_request_tabs.html');
- return this.subject = this["class"].activateTab;
+ this.subject = this.class.activateTab;
});
- it('shows the first tab when action is show', function() {
+ it('shows the first tab when action is show', function () {
this.subject('show');
- return expect($('#notes')).toHaveClass('active');
+ expect($('#notes')).toHaveClass('active');
});
- it('shows the notes tab when action is notes', function() {
+ it('shows the notes tab when action is notes', function () {
this.subject('notes');
- return expect($('#notes')).toHaveClass('active');
+ expect($('#notes')).toHaveClass('active');
});
- it('shows the commits tab when action is commits', function() {
+ it('shows the commits tab when action is commits', function () {
this.subject('commits');
- return expect($('#commits')).toHaveClass('active');
+ expect($('#commits')).toHaveClass('active');
});
- return it('shows the diffs tab when action is diffs', function() {
+ it('shows the diffs tab when action is diffs', function () {
this.subject('diffs');
- return expect($('#diffs')).toHaveClass('active');
+ expect($('#diffs')).toHaveClass('active');
});
});
- return describe('#setCurrentAction', function() {
- beforeEach(function() {
- return this.subject = this["class"].setCurrentAction;
+
+ describe('#setCurrentAction', function () {
+ beforeEach(function () {
+ spyOn($, 'ajax').and.callFake(function () {});
+ this.subject = this.class.setCurrentAction;
});
- it('changes from commits', function() {
- this["class"]._location = stubLocation({
+ it('changes from commits', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1/commits'
});
expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1');
- return expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs');
+ expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs');
});
- it('changes from diffs', function() {
- this["class"]._location = stubLocation({
+ it('changes from diffs', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1/diffs'
});
expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1');
- return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
});
- it('changes from diffs.html', function() {
- this["class"]._location = stubLocation({
+ it('changes from diffs.html', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1/diffs.html'
});
expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1');
- return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
});
- it('changes from notes', function() {
- this["class"]._location = stubLocation({
+ it('changes from notes', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1'
});
expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs');
- return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
});
- it('includes search parameters and hash string', function() {
- this["class"]._location = stubLocation({
+ it('includes search parameters and hash string', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1/diffs',
search: '?view=parallel',
hash: '#L15-35'
});
- return expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35');
+ expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35');
});
- it('replaces the current history state', function() {
- var new_state;
- this["class"]._location = stubLocation({
+ it('replaces the current history state', function () {
+ var newState;
+ setLocation({
pathname: '/foo/bar/merge_requests/1'
});
- new_state = this.subject('commits');
- return expect(this.spies.history).toHaveBeenCalledWith({
+ newState = this.subject('commits');
+ expect(this.spies.history).toHaveBeenCalledWith({
turbolinks: true,
- url: new_state
- }, document.title, new_state);
+ url: newState
+ }, document.title, newState);
});
- return it('treats "show" like "notes"', function() {
- this["class"]._location = stubLocation({
+ it('treats "show" like "notes"', function () {
+ setLocation({
pathname: '/foo/bar/merge_requests/1/commits'
});
- return expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
+ expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
+ });
+ });
+ describe('#loadDiff', function () {
+ it('requires an absolute pathname', function () {
+ spyOn($, 'ajax').and.callFake(function (options) {
+ expect(options.url).toEqual('/foo/bar/merge_requests/1/diffs.json');
+ });
+ this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
});
});
});
-
}).call(this);
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 51f2ae8bcbd..2db182d702b 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -6,17 +6,21 @@
(function() {
window.gon || (window.gon = {});
-
- window.disableButtonIfEmptyField = function() {
- return null;
- };
+ window.gl = window.gl || {};
+ gl.utils = gl.utils || {};
describe('Notes', function() {
- describe('task lists', function() {
- fixture.preload('issue_note.html');
+ var commentsTemplate = 'issues/issue_with_comment.raw';
+ fixture.preload(commentsTemplate);
+ beforeEach(function () {
+ fixture.load(commentsTemplate);
+ gl.utils.disableButtonIfEmptyField = _.noop;
+ window.project_uploads_path = 'http://test.host/uploads';
+ });
+
+ describe('task lists', function() {
beforeEach(function() {
- fixture.load('issue_note.html');
$('form').on('submit', function(e) {
e.preventDefault();
});
@@ -41,12 +45,9 @@
});
describe('comments', function() {
- var commentsTemplate = 'comments.html';
var textarea = '.js-note-text';
- fixture.preload(commentsTemplate);
beforeEach(function() {
- fixture.load(commentsTemplate);
this.notes = new Notes();
this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update');
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 83ebbd63f3a..0a9bc546144 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -34,9 +34,10 @@
};
describe('RightSidebar', function() {
- fixture.preload('right_sidebar.html');
+ var fixtureName = 'issues/open-issue.html.raw';
+ fixture.preload(fixtureName);
beforeEach(function() {
- fixture.load('right_sidebar.html');
+ fixture.load(fixtureName);
this.sidebar = new Sidebar;
$aside = $('.right-sidebar');
$page = $('.page-with-sidebar');
@@ -44,15 +45,12 @@
$toggle = $aside.find('.js-sidebar-toggle');
return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
});
- it('should expand the sidebar when arrow is clicked', function() {
+ it('should expand/collapse the sidebar when arrow is clicked', function() {
+ assertSidebarState('expanded');
$toggle.click();
- return assertSidebarState('expanded');
- });
- it('should collapse the sidebar when arrow is clicked', function() {
+ assertSidebarState('collapsed');
$toggle.click();
assertSidebarState('expanded');
- $toggle.click();
- return assertSidebarState('collapsed');
});
it('should float over the page and when sidebar icons clicked', function() {
$labelsIcon.click();
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index 7d36d79b687..e37816b0a8c 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -4,9 +4,11 @@
(function() {
describe('ShortcutsIssuable', function() {
- fixture.preload('issuable.html');
+ var fixtureName = 'issues/open-issue.html.raw';
+ fixture.preload(fixtureName);
beforeEach(function() {
- fixture.load('issuable.html');
+ fixture.load(fixtureName);
+ document.querySelector('.js-new-note-form').classList.add('js-main-target-form');
return this.shortcut = new ShortcutsIssuable();
});
return describe('#replyWithSelectedText', function() {
diff --git a/spec/javascripts/vue_common_components/commit_spec.js.es6 b/spec/javascripts/vue_common_components/commit_spec.js.es6
index b1dbc8bd5fa..d170517dd9b 100644
--- a/spec/javascripts/vue_common_components/commit_spec.js.es6
+++ b/spec/javascripts/vue_common_components/commit_spec.js.es6
@@ -10,7 +10,7 @@ describe('Commit component', () => {
el: document.querySelector('.test-commit-container'),
propsData: {
tag: false,
- ref: {
+ commit_ref: {
name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
},
@@ -34,7 +34,7 @@ describe('Commit component', () => {
props = {
tag: true,
- ref: {
+ commit_ref: {
name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
},
@@ -59,11 +59,11 @@ describe('Commit component', () => {
});
it('should render a link to the ref url', () => {
- expect(component.$el.querySelector('.branch-name').getAttribute('href')).toEqual(props.ref.ref_url);
+ expect(component.$el.querySelector('.branch-name').getAttribute('href')).toEqual(props.commit_ref.ref_url);
});
it('should render the ref name', () => {
- expect(component.$el.querySelector('.branch-name').textContent).toContain(props.ref.name);
+ expect(component.$el.querySelector('.branch-name').textContent).toContain(props.commit_ref.name);
});
it('should render the commit short sha with a link to the commit url', () => {
@@ -103,7 +103,7 @@ describe('Commit component', () => {
fixture.set('<div class="test-commit-container"></div>');
props = {
tag: false,
- ref: {
+ commit_ref: {
name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
},
diff --git a/spec/lib/banzai/filter/abstract_link_filter_spec.rb b/spec/lib/banzai/filter/abstract_link_filter_spec.rb
index 1ee31a603e4..70a87fbc01e 100644
--- a/spec/lib/banzai/filter/abstract_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/abstract_link_filter_spec.rb
@@ -5,7 +5,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
describe '#references_per_project' do
it 'returns a Hash containing references grouped per project paths' do
- doc = Nokogiri::HTML.fragment("#1 #{project.to_reference}#2")
+ doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2")
filter = described_class.new(doc, project: project)
expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue)
@@ -14,7 +14,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
refs = filter.references_per_project
expect(refs).to be_an_instance_of(Hash)
- expect(refs[project.to_reference]).to eq(Set.new(%w[1 2]))
+ expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2]))
end
end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index e6c90ad87ee..9703e2315b8 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -59,9 +59,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'ignores invalid commit IDs' do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
- expect(project).to receive(:valid_repo?).and_return(true)
- expect(project.repository).to receive(:commit).with(commit1.id.reverse)
- expect(project.repository).to receive(:commit).with(commit2.id)
+ allow(project.repository).to receive(:commit).with(commit1.id.reverse)
expect(reference_filter(act).to_html).to eq exp
end
@@ -100,14 +98,44 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
- let(:project2) { create(:project, :public, namespace: namespace) }
- let(:reference) { range.to_reference(project) }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:project, :public) }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
- before do
- range.project = project2
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}"
+ expect(reference_filter(act).to_html).to eq exp
+
+ exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}"
+ expect(reference_filter(act).to_html).to eq exp
end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -116,24 +144,65 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}")
- expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
+ exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
+ exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
+ expect(reference_filter(act).to_html).to eq exp
+
+ exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:range) { CommitRange.new("#{commit1.id}...master", project) }
let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') }
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index e0f08282551..2e6dcc3a434 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -41,6 +41,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
it 'links with adjacent text' do
doc = reference_filter("See (#{reference}.)")
+
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
end
@@ -48,8 +49,6 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
invalid = invalidate_reference(reference)
exp = act = "See #{invalid}"
- expect(project).to receive(:valid_repo?).and_return(true)
- expect(project.repository).to receive(:commit).with(invalid)
expect(reference_filter(act).to_html).to eq exp
end
@@ -95,34 +94,85 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
- let(:reference) { commit.to_reference(project) }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
- it 'links to a valid reference' do
- doc = reference_filter("See #{reference}")
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+ expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}")
end
- it 'links with adjacent text' do
- doc = reference_filter("Fixed (#{reference}.)")
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Committed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:commit) { project2.commit }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
- exp = Regexp.escape(project2.to_reference)
- expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
+ expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:commit) { project2.commit }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Committed #{invalidate_reference(reference)}"
+
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 8f0b2db3e8e..456dbac0698 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -8,7 +8,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
end
let(:project) { create(:empty_project, :public) }
- let(:issue) { create(:issue, project: project) }
+ let(:issue) { create(:issue, project: project) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
@@ -24,7 +24,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
context 'internal reference' do
it_behaves_like 'a reference containing an element node'
- let(:reference) { issue.to_reference }
+ let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do
allow(project).to receive(:default_issues_tracker?).and_return(false)
@@ -42,7 +42,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+ expect(doc.text).to eql("Fixed (#{reference}.)")
end
it 'ignores invalid issue IDs' do
@@ -116,13 +116,56 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
+ context 'cross-project / cross-namespace complete reference' do
it_behaves_like 'a reference containing an element node'
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
+
+ it 'ignores valid references when cross-reference project uses external tracker' do
+ expect_any_instance_of(described_class).to receive(:find_object).
+ with(project2, issue.iid).
+ and_return(nil)
+
+ exp = act = "Issue #{reference}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)")
+ end
+
+ it 'ignores invalid issue IDs on the referenced project' do
+ exp = act = "Fixed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ it_behaves_like 'a reference containing an element node'
+
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
- let(:reference) { issue.to_reference(project) }
+ let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object).
@@ -140,9 +183,16 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
to eq helper.url_for_issue(issue.iid, project2)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
@@ -150,9 +200,47 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
expect(reference_filter(act).to_html).to eq exp
end
+ end
+
+ context 'cross-project shorthand reference' do
+ it_behaves_like 'a reference containing an element node'
- it 'ignores out-of-bounds issue IDs on the referenced project' do
- exp = act = "Fixed ##{Gitlab::Database::MAX_INT_VALUE + 1}"
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { "#{project2.path}##{issue.iid}" }
+
+ it 'ignores valid references when cross-reference project uses external tracker' do
+ expect_any_instance_of(described_class).to receive(:find_object).
+ with(project2, issue.iid).
+ and_return(nil)
+
+ exp = act = "Issue #{reference}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
+ end
+
+ it 'ignores invalid issue IDs on the referenced project' do
+ exp = act = "Fixed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 9c09f00ae8a..284641fb20a 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -4,7 +4,7 @@ require 'html/pipeline'
describe Banzai::Filter::LabelReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:empty_project, :public, name: 'sample-project') }
let(:label) { create(:label, project: project) }
let(:reference) { label.to_reference }
@@ -48,6 +48,14 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name)
end
+ 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
+
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
@@ -334,14 +342,14 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
context 'with project reference' do
- let(:reference) { project.to_reference + group_label.to_reference(format: :name) }
+ let(:reference) { "#{project.to_reference}#{group_label.to_reference(format: :name)}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
- expect(doc.text).to eq 'See gfm references'
+ expect(doc.text).to eq "See gfm references"
end
it 'links with adjacent text' do
@@ -357,68 +365,247 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
- describe 'cross project label references' do
- 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) }
+ describe 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:empty_project) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
- let!(:result) { reference_filter("See #{reference}") }
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ 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/
- end
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name_with_namespace}"
+ end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
- end
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name_with_namespace}"
end
- context 'project that does not exist referenced' do
- let(:result) { reference_filter('aaa/bbb~ccc') }
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
- it 'does not link reference' do
- expect(result.to_html).to eq 'aaa/bbb~ccc'
- end
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:empty_project, namespace: namespace) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ 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 link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:empty_project, namespace: namespace) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ 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 link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
end
end
describe 'cross group label references' do
- context 'valid project referenced' do
- let(:group) { create(:group) }
- let(:project) { create(:empty_project, :public, namespace: group) }
- let(:another_group) { create(:group) }
- let(:another_project) { create(:empty_project, :public, namespace: another_group) }
- let(:project_name) { another_project.name_with_namespace }
- let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
- let(:reference) { another_project.to_reference + group_label.to_reference }
-
- let!(:result) { reference_filter("See #{reference}", project: project) }
-
- 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: group_label.name)
- end
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_group) { create(:group) }
+ let(:another_project) { create(:empty_project, :public, namespace: another_group) }
+ let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
+ let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
- it 'has valid color' do
- expect(result.css('a span').first.attr('style'))
- .to match /background-color: #00ff00/
- 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: group_label.name)
+ end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{group_label.name} in #{project_name}"
- end
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).
+ to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).
+ to eq "#{group_label.name} in #{another_project.name_with_namespace}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).
+ to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project / same-group_label complete reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ 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: group_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 link text' do
+ expect(result.css('a').first.text).
+ to eq "#{group_label.name} in #{another_project.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).
+ to eq "See #{group_label.name} in #{another_project.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'same project / same group_label complete reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project.namespace,
+ project,
+ label_name: group_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 link text' do
+ expect(result.css('a').first.text).to eq group_label.name
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{group_label.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'same project / same group_label shorthand reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{project.path}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project.namespace,
+ project,
+ label_name: group_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 link text' do
+ expect(result.css('a').first.text).to eq group_label.name
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{group_label.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
end
end
end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 274258a045c..275010c1a2c 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:merge) { create(:merge_request, source_project: project) }
it 'requires project context' do
@@ -86,23 +86,97 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
- let(:project2) { create(:project, :public, namespace: namespace) }
- let(:merge) { create(:merge_request, source_project: project2) }
- let(:reference) { merge.to_reference(project) }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:empty_project, :public) }
+ let(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_merge_request_url(project2.namespace,
- project, merge)
+ project2, merge)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Merge (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eq(reference)
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{reference}.)")
+ end
+
+ it 'ignores invalid merge IDs on the referenced project' do
+ exp = act = "Merge #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_merge_request_url(project2.namespace,
+ project2, merge)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eq("#{project2.path}!#{merge.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{project2.path}!#{merge.iid}.)")
+ end
+
+ it 'ignores invalid merge IDs on the referenced project' do
+ exp = act = "Merge #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path}!#{merge.iid}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_merge_request_url(project2.namespace,
+ project2, merge)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eq("#{project2.path}!#{merge.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{project2.path}!#{merge.iid}.)")
end
it 'ignores invalid merge IDs on the referenced project' do
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 7419863d848..73b5edb99b3 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -148,13 +148,51 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
end
end
- describe 'cross project milestone references' do
- let(:another_project) { create(:empty_project, :public) }
- let(:project_path) { another_project.path_with_namespace }
- let(:milestone) { create(:milestone, project: another_project) }
- let(:reference) { milestone.to_reference(project) }
+ describe 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
- let!(:result) { reference_filter("See #{reference}") }
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(another_project.namespace,
+ another_project,
+ milestone)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path_with_namespace}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path_with_namespace}"
+ end
+ end
+
+ describe 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
expect(result.css('a').first.attr('href')).to eq urls.
@@ -163,14 +201,66 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
milestone)
end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path}"
+ end
+ end
+
+ describe 'cross project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(another_project.namespace,
+ another_project,
+ milestone)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path}.)")
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path}"
end
end
end
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 9b92d1a3926..e036514d283 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -79,11 +79,11 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
- let(:snippet) { create(:project_snippet, project: project2) }
- let(:reference) { snippet.to_reference(project) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -92,9 +92,82 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eql(reference)
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{reference}.)")
+ end
+
+ it 'ignores invalid snippet IDs on the referenced project' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}$#{snippet.id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}$#{snippet.id}.)")
+ end
+
+ it 'ignores invalid snippet IDs on the referenced project' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path}$#{snippet.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}$#{snippet.id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}$#{snippet.id}.)")
end
it 'ignores invalid snippet IDs on the referenced project' do
diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
index 356dd01a03a..70b31f3a880 100644
--- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
+++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
@@ -22,7 +22,7 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
html = header(i, "Header #{i}")
doc = filter(html)
- expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}"
+ expect(doc.css("h#{i} a").first.attr('id')).to eq "user-content-header-#{i}"
end
end
@@ -32,7 +32,12 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
expect(doc.css('h1 a').first.attr('class')).to eq 'anchor'
end
- it 'links to the id' do
+ it 'has a namespaced id' do
+ doc = filter(header(1, 'Header'))
+ expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-header'
+ end
+
+ it 'links to the non-namespaced id' do
doc = filter(header(1, 'Header'))
expect(doc.css('h1 a').first.attr('href')).to eq '#header'
end
@@ -40,29 +45,29 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
describe 'generated IDs' do
it 'translates spaces to dashes' do
doc = filter(header(1, 'This header has spaces in it'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-has-spaces-in-it'
end
it 'squeezes multiple spaces and dashes' do
doc = filter(header(1, 'This---header is poorly-formatted'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-poorly-formatted'
end
it 'removes punctuation' do
doc = filter(header(1, "This, header! is, filled. with @ punctuation?"))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-filled-with-punctuation'
end
it 'appends a unique number to duplicates' do
doc = filter(header(1, 'One') + header(2, 'One'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'one'
- expect(doc.css('h2 a').first.attr('id')).to eq 'one-1'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#one'
+ expect(doc.css('h2 a').first.attr('href')).to eq '#one-1'
end
it 'supports Unicode' do
doc = filter(header(1, '한글'))
- expect(doc.css('h1 a').first.attr('id')).to eq '한글'
+ expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-한글'
expect(doc.css('h1 a').first.attr('href')).to eq '#한글'
end
end
diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb
new file mode 100644
index 00000000000..1b749d1bd39
--- /dev/null
+++ b/spec/lib/gitlab/backup/manager_spec.rb
@@ -0,0 +1,127 @@
+require 'spec_helper'
+
+describe Backup::Manager, lib: true do
+ describe '#remove_old' do
+ let(:progress) { StringIO.new }
+
+ let(:files) do
+ [
+ '1451606400_2016_01_01_gitlab_backup.tar',
+ '1451520000_2015_12_31_gitlab_backup.tar',
+ '1450742400_2015_12_22_gitlab_backup.tar',
+ '1449878400_gitlab_backup.tar',
+ '1449014400_gitlab_backup.tar',
+ 'manual_gitlab_backup.tar'
+ ]
+ end
+
+ before do
+ allow(Dir).to receive(:chdir).and_yield
+ allow(Dir).to receive(:glob).and_return(files)
+ allow(FileUtils).to receive(:rm)
+ allow(Time).to receive(:now).and_return(Time.utc(2016))
+
+ allow(progress).to receive(:puts)
+ allow(progress).to receive(:print)
+
+ allow_any_instance_of(String).to receive(:color) do |string, _color|
+ string
+ end
+
+ @old_progress = $progress # rubocop:disable Style/GlobalVars
+ $progress = progress # rubocop:disable Style/GlobalVars
+ end
+
+ after do
+ $progress = @old_progress # rubocop:disable Style/GlobalVars
+ end
+
+ context 'when keep_time is zero' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(0)
+
+ subject.remove_old
+ end
+
+ it 'removes no files' do
+ expect(FileUtils).not_to have_received(:rm)
+ end
+
+ it 'prints a skipped message' do
+ expect(progress).to have_received(:puts).with('skipping')
+ end
+ end
+
+ context 'when there are no files older than keep_time' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(2592000)
+
+ subject.remove_old
+ end
+
+ it 'removes no files' do
+ expect(FileUtils).not_to have_received(:rm)
+ end
+
+ it 'prints a done message' do
+ expect(progress).to have_received(:puts).with('done. (0 removed)')
+ end
+ end
+
+ context 'when keep_time is set to remove files' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(1)
+
+ subject.remove_old
+ end
+
+ it 'removes matching files with a human-readable timestamp' do
+ expect(FileUtils).to have_received(:rm).with(files[1])
+ expect(FileUtils).to have_received(:rm).with(files[2])
+ end
+
+ it 'removes matching files without a human-readable timestamp' do
+ expect(FileUtils).to have_received(:rm).with(files[3])
+ expect(FileUtils).to have_received(:rm).with(files[4])
+ end
+
+ it 'does not remove files that are not old enough' do
+ expect(FileUtils).not_to have_received(:rm).with(files[0])
+ end
+
+ it 'does not remove non-matching files' do
+ expect(FileUtils).not_to have_received(:rm).with(files[5])
+ end
+
+ it 'prints a done message' do
+ expect(progress).to have_received(:puts).with('done. (4 removed)')
+ end
+ end
+
+ context 'when removing a file fails' do
+ let(:file) { files[1] }
+ let(:message) { "Permission denied @ unlink_internal - #{file}" }
+
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(1)
+ allow(FileUtils).to receive(:rm).with(file).and_raise(Errno::EACCES, message)
+
+ subject.remove_old
+ end
+
+ it 'removes the remaining expected files' do
+ expect(FileUtils).to have_received(:rm).with(files[2])
+ expect(FileUtils).to have_received(:rm).with(files[3])
+ expect(FileUtils).to have_received(:rm).with(files[4])
+ end
+
+ it 'sets the correct removed count' do
+ expect(progress).to have_received(:puts).with('done. (3 removed)')
+ end
+
+ it 'prints the error from file that could not be removed' do
+ expect(progress).to have_received(:puts).with(a_string_matching(message))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/issue_create_spec.rb b/spec/lib/gitlab/chat_commands/issue_create_spec.rb
index dd07cff9243..6c71e79ff6d 100644
--- a/spec/lib/gitlab/chat_commands/issue_create_spec.rb
+++ b/spec/lib/gitlab/chat_commands/issue_create_spec.rb
@@ -57,5 +57,12 @@ describe Gitlab::ChatCommands::IssueCreate, service: true do
expect(match[:title]).to eq('my title')
expect(match[:description]).to eq('description')
end
+
+ it 'matches the alias new' do
+ match = described_class.match("issue new my title")
+
+ expect(match).not_to be_nil
+ expect(match[:title]).to eq('my title')
+ end
end
end
diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb
new file mode 100644
index 00000000000..619ecbcba67
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/canceled_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Canceled do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'canceled' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'canceled' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_canceled' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: canceled' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb
new file mode 100644
index 00000000000..157302c65a8
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/created_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Created do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'created' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'created' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_created' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: created' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb
new file mode 100644
index 00000000000..120e121aae5
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/extended_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Extended do
+ subject do
+ Class.new.extend(described_class)
+ end
+
+ it 'requires subclass to implement matcher' do
+ expect { subject.matches?(double) }
+ .to raise_error(NotImplementedError)
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb
new file mode 100644
index 00000000000..0b3cb8168e6
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/failed_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Failed do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'failed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'failed' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_failed' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: failed' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb
new file mode 100644
index 00000000000..57c901c1202
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pending_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pending do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'pending' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'pending' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_pending' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: pending' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
new file mode 100644
index 00000000000..21adee3f8e7
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::Common do
+ let(:pipeline) { create(:ci_pipeline) }
+
+ subject do
+ Class.new(Gitlab::Ci::Status::Core)
+ .new(pipeline).extend(described_class)
+ end
+
+ it 'does not have action' do
+ expect(subject).not_to have_action
+ end
+
+ it 'has details' do
+ expect(subject).to have_details
+ end
+
+ it 'links to the pipeline details page' do
+ expect(subject.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
new file mode 100644
index 00000000000..d6243940f2e
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::Factory do
+ subject do
+ described_class.new(pipeline)
+ end
+
+ let(:status) do
+ subject.fabricate!
+ end
+
+ context 'when pipeline has a core status' do
+ HasStatus::AVAILABLE_STATUSES.each do |core_status|
+ context "when core status is #{core_status}" do
+ let(:pipeline) do
+ create(:ci_pipeline, status: core_status)
+ end
+
+ it "fabricates a core status #{core_status}" do
+ expect(status).to be_a(
+ Gitlab::Ci::Status.const_get(core_status.capitalize))
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ expect(status).not_to have_action
+ expect(status.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
+ end
+ end
+
+ context 'when pipeline has warnings' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :success)
+ end
+
+ before do
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
+ end
+
+ it 'fabricates extended "success with warnings" status' do
+ expect(status)
+ .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb
new file mode 100644
index 00000000000..02e526e3de2
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
+ subject do
+ described_class.new(double('status'))
+ end
+
+ describe '#test' do
+ it { expect(subject.text).to eq 'passed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'passed with warnings' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_warning' }
+ end
+
+ describe '.matches?' do
+ context 'when pipeline is successful' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :success)
+ end
+
+ context 'when pipeline has warnings' do
+ before do
+ allow(pipeline).to receive(:has_warnings?).and_return(true)
+ end
+
+ it 'is a correct match' do
+ expect(described_class.matches?(pipeline)).to eq true
+ end
+ end
+
+ context 'when pipeline does not have warnings' do
+ it 'does not match' do
+ expect(described_class.matches?(pipeline)).to eq false
+ end
+ end
+ end
+
+ context 'when pipeline is not successful' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :skipped)
+ end
+
+ context 'when pipeline has warnings' do
+ before do
+ allow(pipeline).to receive(:has_warnings?).and_return(true)
+ end
+
+ it 'does not match' do
+ expect(described_class.matches?(pipeline)).to eq false
+ end
+ end
+
+ context 'when pipeline does not have warnings' do
+ it 'does not match' do
+ expect(described_class.matches?(pipeline)).to eq false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb
new file mode 100644
index 00000000000..c023f1872cc
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/running_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Running do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'running' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'running' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_running' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: running' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb
new file mode 100644
index 00000000000..d4f7f4b3b70
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/skipped_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Skipped do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'skipped' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'skipped' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_skipped' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: skipped' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb
new file mode 100644
index 00000000000..9e261a3aa5f
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/success_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Success do
+ subject { described_class.new(double('subject')) }
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'passed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'passed' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_success' }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq 'Double: passed' }
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index 9aeaa6b3ee8..6062e7af4f5 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -321,6 +321,6 @@ describe Gitlab::CycleAnalytics::Events do
context.update(milestone: milestone)
mr = create_merge_request_closing_issue(context)
- ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha)
+ ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
end
end
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
index dc4f7dc69db..2d85e712db0 100644
--- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::CycleAnalytics::Permissions do
- let(:project) { create(:empty_project) }
+ let(:project) { create(:empty_project, public_builds: false) }
let(:user) { create(:user) }
subject { described_class.get(user: user, project: project) }
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index c863a5f04cc..2a680f03476 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -10,4 +10,12 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
end
+
+ it 'does not hightlight file if blob is not accessable' do
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
+
+ expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+
+ described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ end
end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 6b3dfebd85d..d619e401897 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -64,7 +64,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'description with project labels' do
let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
- let(:project_ref) { old_project.to_reference }
+ let(:project_ref) { old_project.to_reference(new_project) }
context 'label referenced by id' do
let(:text) { '#1 and ~123' }
@@ -80,7 +80,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'description with group labels' do
let(:old_group) { create(:group) }
let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) }
- let(:project_ref) { old_project.to_reference }
+ let(:project_ref) { old_project.to_reference(new_project) }
before do
old_project.update(namespace: old_group)
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 576aa5c366f..578db51631e 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -26,4 +26,29 @@ describe Gitlab::GitAccessWiki, lib: true do
def changes
['6f6d7e7ed 570e7b2ab refs/heads/master']
end
+
+ describe '#download_access_check' do
+ subject { access.check('git-upload-pack', '_any') }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when wiki feature is enabled' do
+ it 'give access to download wiki code' do
+ project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED)
+
+ expect(subject.allowed?).to be_truthy
+ end
+ end
+
+ context 'when wiki feature is disabled' do
+ it 'does not give access to download wiki code' do
+ project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
+
+ expect(subject.allowed?).to be_falsey
+ expect(subject.message).to match(/You are not allowed to download code/)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
index e5300dbba1e..462caa5b5fe 100644
--- a/spec/lib/gitlab/github_import/branch_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
@@ -49,14 +49,20 @@ describe Gitlab::GithubImport::BranchFormatter, lib: true do
end
describe '#valid?' do
- it 'returns true when raw repo is present' do
+ it 'returns true when raw sha and ref are present' do
branch = described_class.new(project, double(raw))
expect(branch.valid?).to eq true
end
- it 'returns false when raw repo is blank' do
- branch = described_class.new(project, double(raw.merge(repo: nil)))
+ it 'returns false when raw sha is blank' do
+ branch = described_class.new(project, double(raw.merge(sha: nil)))
+
+ expect(branch.valid?).to eq false
+ end
+
+ it 'returns false when raw ref is blank' do
+ branch = described_class.new(project, double(raw.merge(ref: nil)))
expect(branch.valid?).to eq false
end
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 000b9aa6f83..9e027839f59 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -155,7 +155,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
message: 'The remote data could not be fully imported.',
errors: [
{ type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" },
- { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank, Title is too short (minimum is 0 characters)" },
+ { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" },
{ type: :wiki, errors: "Gitlab::Shell::Error" },
{ type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" }
]
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 770e8b0c2f4..1cb02f8e318 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true do
describe 'map members' do
- let(:user) { create(:user) }
+ let(:user) { create(:user, authorized_projects_populated: true) }
let(:project) { create(:project, :public, name: 'searchable_project') }
- let(:user2) { create(:user) }
+ let(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 }
let(:exported_members) do
[{
@@ -67,5 +67,12 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(ProjectMember.find_by_invite_email('invite@test.com')).not_to be_nil
end
+
+ it 'authorizes the users to the project' do
+ members_mapper.map
+
+ expect(user.authorized_project?(project)).to be true
+ expect(user2.authorized_project?(project)).to be true
+ end
end
end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
new file mode 100644
index 00000000000..52428547a9f
--- /dev/null
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -0,0 +1,194 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb')
+
+describe MigrateProcessCommitWorkerJobs do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:commit) { project.commit.raw.raw_commit }
+
+ describe 'Project' do
+ describe 'find_including_path' do
+ it 'returns Project instances' do
+ expect(described_class::Project.find_including_path(project.id)).
+ to be_an_instance_of(described_class::Project)
+ end
+
+ it 'selects the full path for every Project' do
+ migration_project = described_class::Project.
+ find_including_path(project.id)
+
+ expect(migration_project[:path_with_namespace]).
+ to eq(project.path_with_namespace)
+ end
+ end
+
+ describe '#repository_storage_path' do
+ it 'returns the storage path for the repository' do
+ migration_project = described_class::Project.
+ find_including_path(project.id)
+
+ expect(File.directory?(migration_project.repository_storage_path)).
+ to eq(true)
+ end
+ end
+
+ describe '#repository_path' do
+ it 'returns the path to the repository' do
+ migration_project = described_class::Project.
+ find_including_path(project.id)
+
+ expect(File.directory?(migration_project.repository_path)).to eq(true)
+ end
+ end
+
+ describe '#repository' do
+ it 'returns a Rugged::Repository' do
+ migration_project = described_class::Project.
+ find_including_path(project.id)
+
+ expect(migration_project.repository).
+ to be_an_instance_of(Rugged::Repository)
+ end
+ end
+ end
+
+ describe '#up', :redis do
+ let(:migration) { described_class.new }
+
+ def job_count
+ Sidekiq.redis { |r| r.llen('queue:process_commit') }
+ end
+
+ before do
+ Sidekiq.redis do |redis|
+ job = JSON.dump(args: [project.id, user.id, commit.oid])
+ redis.lpush('queue:process_commit', job)
+ end
+ end
+
+ it 'skips jobs using a project that no longer exists' do
+ allow(described_class::Project).to receive(:find_including_path).
+ with(project.id).
+ and_return(nil)
+
+ migration.up
+
+ expect(job_count).to eq(0)
+ end
+
+ it 'skips jobs using commits that no longer exist' do
+ allow_any_instance_of(Rugged::Repository).to receive(:lookup).
+ with(commit.oid).
+ and_raise(Rugged::OdbError)
+
+ migration.up
+
+ expect(job_count).to eq(0)
+ end
+
+ it 'inserts migrated jobs back into the queue' do
+ migration.up
+
+ expect(job_count).to eq(1)
+ end
+
+ context 'a migrated job' do
+ let(:job) do
+ migration.up
+
+ JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') })
+ end
+
+ let(:commit_hash) do
+ job['args'][2]
+ end
+
+ it 'includes the project ID' do
+ expect(job['args'][0]).to eq(project.id)
+ end
+
+ it 'includes the user ID' do
+ expect(job['args'][1]).to eq(user.id)
+ end
+
+ it 'includes the commit ID' do
+ expect(commit_hash['id']).to eq(commit.oid)
+ end
+
+ it 'includes the commit message' do
+ expect(commit_hash['message']).to eq(commit.message)
+ end
+
+ it 'includes the parent IDs' do
+ expect(commit_hash['parent_ids']).to eq(commit.parent_ids)
+ end
+
+ it 'includes the author date' do
+ expect(commit_hash['authored_date']).to eq(commit.author[:time].to_s)
+ end
+
+ it 'includes the author name' do
+ expect(commit_hash['author_name']).to eq(commit.author[:name])
+ end
+
+ it 'includes the author Email' do
+ expect(commit_hash['author_email']).to eq(commit.author[:email])
+ end
+
+ it 'includes the commit date' do
+ expect(commit_hash['committed_date']).to eq(commit.committer[:time].to_s)
+ end
+
+ it 'includes the committer name' do
+ expect(commit_hash['committer_name']).to eq(commit.committer[:name])
+ end
+
+ it 'includes the committer Email' do
+ expect(commit_hash['committer_email']).to eq(commit.committer[:email])
+ end
+ end
+ end
+
+ describe '#down', :redis do
+ let(:migration) { described_class.new }
+
+ def job_count
+ Sidekiq.redis { |r| r.llen('queue:process_commit') }
+ end
+
+ before do
+ Sidekiq.redis do |redis|
+ job = JSON.dump(args: [project.id, user.id, commit.oid])
+ redis.lpush('queue:process_commit', job)
+
+ migration.up
+ end
+ end
+
+ it 'pushes migrated jobs back into the queue' do
+ migration.down
+
+ expect(job_count).to eq(1)
+ end
+
+ context 'a migrated job' do
+ let(:job) do
+ migration.down
+
+ JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') })
+ end
+
+ it 'includes the project ID' do
+ expect(job['args'][0]).to eq(project.id)
+ end
+
+ it 'includes the user ID' do
+ expect(job['args'][1]).to eq(user.id)
+ end
+
+ it 'includes the commit SHA' do
+ expect(job['args'][2]).to eq(commit.oid)
+ end
+ end
+ end
+end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index ef07f2275b1..d4970e38f7c 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -730,8 +730,8 @@ describe Ci::Build, models: true do
pipeline2 = create(:ci_pipeline, project: project)
@build2 = create(:ci_build, pipeline: pipeline2)
- commits = [double(id: pipeline.sha), double(id: pipeline2.sha)]
- allow(@merge_request).to receive(:commits).and_return(commits)
+ allow(@merge_request).to receive(:commits_sha).
+ and_return([pipeline.sha, pipeline2.sha])
allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 3b12f16b4db..3f93d9ddf19 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Ci::Pipeline, models: true do
+ include EmailHelpers
+
let(:project) { FactoryGirl.create :empty_project }
let(:pipeline) { FactoryGirl.create :ci_empty_pipeline, status: 'created', project: project }
@@ -402,6 +404,76 @@ describe Ci::Pipeline, models: true do
end
end
+ describe '#detailed_status' do
+ context 'when pipeline is created' do
+ let(:pipeline) { create(:ci_pipeline, status: :created) }
+
+ it 'returns detailed status for created pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'created'
+ end
+ end
+
+ context 'when pipeline is pending' do
+ let(:pipeline) { create(:ci_pipeline, status: :pending) }
+
+ it 'returns detailed status for pending pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'pending'
+ end
+ end
+
+ context 'when pipeline is running' do
+ let(:pipeline) { create(:ci_pipeline, status: :running) }
+
+ it 'returns detailed status for running pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'running'
+ end
+ end
+
+ context 'when pipeline is successful' do
+ let(:pipeline) { create(:ci_pipeline, status: :success) }
+
+ it 'returns detailed status for successful pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'passed'
+ end
+ end
+
+ context 'when pipeline is failed' do
+ let(:pipeline) { create(:ci_pipeline, status: :failed) }
+
+ it 'returns detailed status for failed pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'failed'
+ end
+ end
+
+ context 'when pipeline is canceled' do
+ let(:pipeline) { create(:ci_pipeline, status: :canceled) }
+
+ it 'returns detailed status for canceled pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'canceled'
+ end
+ end
+
+ context 'when pipeline is skipped' do
+ let(:pipeline) { create(:ci_pipeline, status: :skipped) }
+
+ it 'returns detailed status for skipped pipeline' do
+ expect(pipeline.detailed_status.text).to eq 'skipped'
+ end
+ end
+
+ context 'when pipeline is successful but with warnings' do
+ let(:pipeline) { create(:ci_pipeline, status: :success) }
+
+ before do
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
+ end
+
+ it 'retruns detailed status for successful pipeline with warnings' do
+ expect(pipeline.detailed_status.label).to eq 'passed with warnings'
+ end
+ end
+ end
+
describe '#cancelable?' do
%i[created running pending].each do |status0|
context "when there is a build #{status0}" do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 4e7833c3162..bee9f714849 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -5,6 +5,13 @@ describe Ci::Variable, models: true do
let(:secret_value) { 'secret' }
+ it { is_expected.to validate_presence_of(:key) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:gl_project_id) }
+ it { is_expected.to validate_length_of(:key).is_at_most(255) }
+ it { is_expected.to allow_value('foo').for(:key) }
+ it { is_expected.not_to allow_value('foo bar').for(:key) }
+ it { is_expected.not_to allow_value('foo/bar').for(:key) }
+
before :each do
subject.value = secret_value
end
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index c41359b55a3..d89d4342dea 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -45,7 +45,7 @@ describe CommitRange, models: true do
end
describe '#to_reference' do
- let(:cross) { create(:project) }
+ let(:cross) { create(:empty_project, namespace: project.namespace) }
it 'returns a String reference to the object' do
expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}"
@@ -56,12 +56,12 @@ describe CommitRange, models: true do
end
it 'supports a cross-project reference' do
- expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}"
+ expect(range.to_reference(cross)).to eq "#{project.path}@#{full_sha_from}...#{full_sha_to}"
end
end
describe '#reference_link_text' do
- let(:cross) { create(:project) }
+ let(:cross) { create(:empty_project, namespace: project.namespace) }
it 'returns a String reference to the object' do
expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}"
@@ -72,7 +72,7 @@ describe CommitRange, models: true do
end
it 'supports a cross-project reference' do
- expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}"
+ expect(range.reference_link_text(cross)).to eq "#{project.path}@#{sha_from}...#{sha_to}"
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index e3bb3482d67..eb482c7f913 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -34,24 +34,30 @@ describe Commit, models: true do
end
describe '#to_reference' do
+ let(:project) { create(:project, path: 'sample-project') }
+ let(:commit) { project.commit }
+
it 'returns a String reference to the object' do
expect(commit.to_reference).to eq commit.id
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(commit.to_reference(another_project)).to eq "sample-project@#{commit.id}"
end
end
describe '#reference_link_text' do
+ let(:project) { create(:project, path: 'sample-project') }
+ let(:commit) { project.commit }
+
it 'returns a String reference to the object' do
expect(commit.reference_link_text).to eq commit.short_id
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(commit.reference_link_text(another_project)).to eq "sample-project@#{commit.short_id}"
end
end
@@ -302,4 +308,21 @@ eos
expect(commit.uri_type('this/path/doesnt/exist')).to be_nil
end
end
+
+ describe '.from_hash' do
+ let(:new_commit) { described_class.from_hash(commit.to_hash, project) }
+
+ it 'returns a Commit' do
+ expect(new_commit).to be_an_instance_of(described_class)
+ end
+
+ it 'wraps a Gitlab::Git::Commit' do
+ expect(new_commit.raw).to be_an_instance_of(Gitlab::Git::Commit)
+ end
+
+ it 'stores the correct commit fields' do
+ expect(new_commit.id).to eq(commit.id)
+ expect(new_commit.message).to eq(commit.message)
+ end
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 6f84bffe046..4fa06a8c60a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -35,7 +35,7 @@ describe Issue, "Issuable" do
it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to validate_length_of(:title).is_at_least(0).is_at_most(255) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
end
describe "Scope" do
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 2a67c60b978..bc32fadd391 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -521,6 +521,15 @@ describe Discussion, model: true do
end
end
+ describe "#first_note_to_resolve" do
+ it "returns the first not that still needs to be resolved" do
+ allow(first_note).to receive(:to_be_resolved?).and_return(false)
+ allow(second_note).to receive(:to_be_resolved?).and_return(true)
+
+ expect(subject.first_note_to_resolve).to eq(second_note)
+ end
+ end
+
describe "#collapsed?" do
context "when a diff discussion" do
before do
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index d06665197db..c8170164898 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -13,9 +13,9 @@ describe Environment, models: true do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
- it { is_expected.to validate_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
- it { is_expected.to validate_length_of(:external_url).is_within(0..255) }
+ it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
# To circumvent a not null violation of the name column:
# https://github.com/thoughtbot/shoulda-matchers/issues/336
diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb
index 2369658bf78..668aa6fb357 100644
--- a/spec/models/group_label_spec.rb
+++ b/spec/models/group_label_spec.rb
@@ -37,6 +37,16 @@ describe GroupLabel, models: true do
end
end
+ context 'cross-project' do
+ let(:namespace) { build_stubbed(:namespace) }
+ let(:source_project) { build_stubbed(:empty_project, name: 'project-1', namespace: namespace) }
+ let(:target_project) { build_stubbed(:empty_project, name: 'project-2', namespace: namespace) }
+
+ it 'returns a String reference to the object' do
+ expect(label.to_reference(source_project, target_project)).to eq %(project-1~#{label.id})
+ end
+ end
+
context 'using invalid format' do
it 'raises error' do
expect { label.to_reference(format: :invalid) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 89e93dce8c5..24e216329a9 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -43,14 +43,16 @@ describe Issue, models: true do
end
describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:issue) { build(:issue, iid: 1, project: project) }
+
it 'returns a String reference to the object' do
- expect(subject.to_reference).to eq "##{subject.iid}"
+ expect(issue.to_reference).to eq "#1"
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(subject.to_reference(cross)).
- to eq "#{subject.project.to_reference}##{subject.iid}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(issue.to_reference(another_project)).to eq "sample-project#1"
end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 90731f55470..2a33d819138 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -7,9 +7,13 @@ describe Key, models: true do
describe "Validation" do
it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
+
it { is_expected.to validate_presence_of(:key) }
- it { is_expected.to validate_length_of(:title).is_within(0..255) }
- it { is_expected.to validate_length_of(:key).is_within(0..5000) }
+ it { is_expected.to validate_length_of(:key).is_at_most(5000) }
+ it { is_expected.to allow_value('ssh-foo').for(:key) }
+ it { is_expected.to allow_value('ecdsa-foo').for(:key) }
+ it { is_expected.not_to allow_value('foo-bar').for(:key) }
end
describe "Methods" do
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index e5007424041..eb876d105da 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -77,24 +77,13 @@ describe MergeRequestDiff, models: true do
end
describe '#commits_sha' do
- shared_examples 'returning all commits SHA' do
- it 'returns all commits SHA' do
- commits_sha = subject.commits_sha
+ it 'returns all commits SHA using serialized commits' do
+ subject.st_commits = [
+ { id: 'sha1' },
+ { id: 'sha2' }
+ ]
- expect(commits_sha).to eq(subject.commits.map(&:sha))
- end
- end
-
- context 'when commits were loaded' do
- before do
- subject.commits
- end
-
- it_behaves_like 'returning all commits SHA'
- end
-
- context 'when commits were not loaded' do
- it_behaves_like 'returning all commits SHA'
+ expect(subject.commits_sha).to eq(['sha1', 'sha2'])
end
end
@@ -113,4 +102,15 @@ describe MergeRequestDiff, models: true do
expect(diffs.size).to eq(3)
end
end
+
+ describe '#commits_count' do
+ it 'returns number of commits using serialized commits' do
+ subject.st_commits = [
+ { id: 'sha1' },
+ { id: 'sha2' }
+ ]
+
+ expect(subject.commits_count).to eq 2
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 58ccd056328..8b730be91fd 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -31,7 +31,7 @@ describe MergeRequest, models: true do
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
- context "Validation of merge user with Merge When Build succeeds" do
+ context "Validation of merge user with Merge When Pipeline Succeeds" do
it "allows user to be nil when the feature is disabled" do
expect(subject).to be_valid
end
@@ -142,13 +142,16 @@ describe MergeRequest, models: true do
end
describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:merge_request) { build(:merge_request, target_project: project, iid: 1) }
+
it 'returns a String reference to the object' do
- expect(subject.to_reference).to eq "!#{subject.iid}"
+ expect(merge_request.to_reference).to eq "!1"
end
it 'supports a cross-project reference' do
- cross = double('project')
- expect(subject.to_reference(cross)).to eq "#{subject.source_project.to_reference}!#{subject.iid}"
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
end
end
@@ -557,20 +560,17 @@ describe MergeRequest, models: true do
end
describe '#commits_sha' do
- let(:commit0) { double('commit0', sha: 'sha1') }
- let(:commit1) { double('commit1', sha: 'sha2') }
- let(:commit2) { double('commit2', sha: 'sha3') }
-
before do
- allow(subject.merge_request_diff).to receive(:commits).and_return([commit0, commit1, commit2])
+ allow(subject.merge_request_diff).to receive(:commits_sha).
+ and_return(['sha1'])
end
- it 'returns sha of commits' do
- expect(subject.commits_sha).to contain_exactly('sha1', 'sha2', 'sha3')
+ it 'delegates to merge request diff' do
+ expect(subject.commits_sha).to eq ['sha1']
end
end
- describe '#pipeline' do
+ describe '#head_pipeline' do
describe 'when the source project exists' do
it 'returns the latest pipeline' do
pipeline = double(:ci_pipeline, ref: 'master')
@@ -581,7 +581,7 @@ describe MergeRequest, models: true do
with('master', '123abc').
and_return(pipeline)
- expect(subject.pipeline).to eq(pipeline)
+ expect(subject.head_pipeline).to eq(pipeline)
end
end
@@ -589,7 +589,7 @@ describe MergeRequest, models: true do
it 'returns nil' do
allow(subject).to receive(:source_project).and_return(nil)
- expect(subject.pipeline).to be_nil
+ expect(subject.head_pipeline).to be_nil
end
end
end
@@ -857,7 +857,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.update(status: 'failed')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_falsey }
@@ -866,7 +866,7 @@ describe MergeRequest, models: true do
context 'and a successful pipeline is associated' do
before do
pipeline.update(status: 'success')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -875,7 +875,7 @@ describe MergeRequest, models: true do
context 'and a skipped pipeline is associated' do
before do
pipeline.update(status: 'skipped')
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -883,7 +883,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -896,7 +896,7 @@ describe MergeRequest, models: true do
context 'and a failed pipeline is associated' do
before do
pipeline.statuses << create(:commit_status, status: 'failed', project: project)
- allow(subject).to receive(:pipeline) { pipeline }
+ allow(subject).to receive(:head_pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -904,7 +904,7 @@ describe MergeRequest, models: true do
context 'when no pipeline is associated' do
before do
- allow(subject).to receive(:pipeline) { nil }
+ allow(subject).to receive(:head_pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
@@ -1127,6 +1127,46 @@ describe MergeRequest, models: true do
allow(subject).to receive(:diff_discussions).and_return([first_discussion, second_discussion, third_discussion])
end
+ describe '#resolvable_discussions' do
+ before do
+ allow(first_discussion).to receive(:to_be_resolved?).and_return(true)
+ allow(second_discussion).to receive(:to_be_resolved?).and_return(false)
+ allow(third_discussion).to receive(:to_be_resolved?).and_return(false)
+ end
+
+ it 'includes only discussions that need to be resolved' do
+ expect(subject.resolvable_discussions).to eq([first_discussion])
+ end
+ end
+
+ describe '#discussions_can_be_resolved_by? user' do
+ let(:user) { build(:user) }
+
+ context 'all discussions can be resolved by the user' do
+ before do
+ allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(third_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ end
+
+ it 'allows a user to resolve the discussions' do
+ expect(subject.discussions_can_be_resolved_by?(user)).to be(true)
+ end
+ end
+
+ context 'one discussion cannot be resolved by the user' do
+ before do
+ allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true)
+ allow(third_discussion).to receive(:can_resolve?).with(user).and_return(false)
+ end
+
+ it 'allows a user to resolve the discussions' do
+ expect(subject.discussions_can_be_resolved_by?(user)).to be(false)
+ end
+ end
+ end
+
describe "#discussions_resolvable?" do
context "when all discussions are unresolvable" do
before do
@@ -1440,4 +1480,26 @@ describe MergeRequest, models: true do
end
end
end
+
+ describe '#has_commits?' do
+ before do
+ allow(subject.merge_request_diff).to receive(:commits_count).
+ and_return(2)
+ end
+
+ it 'returns true when merge request diff has commits' do
+ expect(subject.has_commits?).to be_truthy
+ end
+ end
+
+ describe '#has_no_commits?' do
+ before do
+ allow(subject.merge_request_diff).to receive(:commits_count).
+ and_return(0)
+ end
+
+ it 'returns true when merge request diff has 0 commits' do
+ expect(subject.has_no_commits?).to be_truthy
+ end
+ end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index a4bfe851dfb..0cc2efae5f9 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -246,4 +246,18 @@ describe Milestone, models: true do
end
end
end
+
+ describe '#to_reference' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:milestone) { build(:milestone, iid: 1, project: project) }
+
+ it 'returns a String reference to the object' do
+ expect(milestone.to_reference).to eq "%1"
+ end
+
+ it 'supports a cross-project reference' do
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(milestone.to_reference(another_project)).to eq "sample-project%1"
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 431b3e4435f..ba0ed4a3603 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -4,11 +4,18 @@ describe Namespace, models: true do
let!(:namespace) { create(:namespace) }
it { is_expected.to have_many :projects }
- it { is_expected.to validate_presence_of :name }
+
+ it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
- it { is_expected.to validate_presence_of :path }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
+
+ it { is_expected.to validate_length_of(:description).is_at_most(255) }
+
+ it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path) }
- it { is_expected.to validate_presence_of :owner }
+ it { is_expected.to validate_length_of(:path).is_at_most(255) }
+
+ it { is_expected.to validate_presence_of(:owner) }
describe "Mass assignment" do
end
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
index 18c9d449ee5..4d538cac007 100644
--- a/spec/models/project_label_spec.rb
+++ b/spec/models/project_label_spec.rb
@@ -105,14 +105,14 @@ describe ProjectLabel, models: true do
context 'using name' do
it 'returns cross reference with label name' do
expect(label.to_reference(project, format: :name))
- .to eq %Q(#{label.project.to_reference}~"#{label.name}")
+ .to eq %Q(#{label.project.path_with_namespace}~"#{label.name}")
end
end
context 'using id' do
it 'returns cross reference with label id' do
expect(label.to_reference(project, format: :id))
- .to eq %Q(#{label.project.to_reference}~#{label.id})
+ .to eq %Q(#{label.project.path_with_namespace}~#{label.id})
end
end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index f5da967cd14..862e3a72a73 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -68,7 +68,7 @@ describe JiraService, models: true do
end
end
- describe "Execute" do
+ describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -101,12 +101,10 @@ describe JiraService, models: true do
@jira_service.save
project_issues_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123'
- @project_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/project/GitLabProject'
@transitions_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/transitions'
@comment_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/comment'
@remote_link_url = 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/issue/JIRA-123/remotelink'
- WebMock.stub_request(:get, @project_url)
WebMock.stub_request(:get, project_issues_url)
WebMock.stub_request(:post, @transitions_url)
WebMock.stub_request(:post, @comment_url)
@@ -114,7 +112,7 @@ describe JiraService, models: true do
end
it "calls JIRA API" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /Issue solved with/
@@ -124,7 +122,7 @@ describe JiraService, models: true do
# Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
# for more information
it "creates Remote Link reference in JIRA for comment" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
# Creates comment
expect(WebMock).to have_requested(:post, @comment_url)
@@ -146,7 +144,7 @@ describe JiraService, models: true do
it "does not send comment or remote links to issues already closed" do
allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(true)
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).not_to have_requested(:post, @comment_url)
expect(WebMock).not_to have_requested(:post, @remote_link_url)
@@ -155,7 +153,7 @@ describe JiraService, models: true do
it "references the GitLab commit/merge request" do
stub_config_setting(base_url: custom_base_url)
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /#{custom_base_url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
@@ -170,7 +168,7 @@ describe JiraService, models: true do
{ script_name: '/gitlab' }
end
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
@@ -178,19 +176,33 @@ describe JiraService, models: true do
end
it "calls the api with jira_issue_transition_id" do
- @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+ @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @transitions_url).with(
body: /custom-id/
).once
end
+ end
- context "when testing" do
- it "tries to get jira project" do
- @jira_service.execute(nil)
+ describe '#test_settings' do
+ let(:jira_service) do
+ described_class.new(
+ url: 'http://jira.example.com',
+ username: 'gitlab_jira_username',
+ password: 'gitlab_jira_password',
+ project_key: 'GitLabProject'
+ )
+ end
+ let(:project_url) { 'http://gitlab_jira_username:gitlab_jira_password@jira.example.com/rest/api/2/project/GitLabProject' }
- expect(WebMock).to have_requested(:get, @project_url)
- end
+ before do
+ WebMock.stub_request(:get, project_url)
+ end
+
+ it 'tries to get JIRA project' do
+ jira_service.test_settings
+
+ expect(WebMock).to have_requested(:get, project_url)
end
end
diff --git a/spec/models/project_services/pipeline_email_service_spec.rb b/spec/models/project_services/pipeline_email_service_spec.rb
index 4f56bceda44..7c8824485f5 100644
--- a/spec/models/project_services/pipeline_email_service_spec.rb
+++ b/spec/models/project_services/pipeline_email_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe PipelinesEmailService do
+ include EmailHelpers
+
let(:pipeline) do
create(:ci_pipeline, project: project, sha: project.commit('master').sha)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8abcce42ce0..4d57330ed1c 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -131,14 +131,18 @@ describe Project, models: true do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
- it { is_expected.to validate_length_of(:name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
- it { is_expected.to validate_length_of(:path).is_within(0..255) }
- it { is_expected.to validate_length_of(:description).is_within(0..2000) }
+ it { is_expected.to validate_length_of(:path).is_at_most(255) }
+
+ it { is_expected.to validate_length_of(:description).is_at_most(2000) }
+
it { is_expected.to validate_presence_of(:creator) }
+
it { is_expected.to validate_presence_of(:namespace) }
+
it { is_expected.to validate_presence_of(:repository_storage) }
it 'does not allow new projects beyond user limits' do
@@ -258,10 +262,70 @@ describe Project, models: true do
end
describe '#to_reference' do
- let(:project) { create(:empty_project) }
+ let(:owner) { create(:user, name: 'Gitlab') }
+ let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
+ let(:project) { create(:empty_project, path: 'sample-project', namespace: namespace) }
- it 'returns a String reference to the object' do
- expect(project.to_reference).to eq project.path_with_namespace
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_reference).to be_nil
+ end
+ end
+
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_reference(project)).to be_nil
+ end
+ end
+
+ context 'when cross namespace project argument' do
+ let(:another_namespace_project) { create(:empty_project, name: 'another-project') }
+
+ it 'returns complete path to the project' do
+ expect(project.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project'
+ end
+ end
+
+ context 'when same namespace / cross-project argument' do
+ let(:another_project) { create(:empty_project, namespace: namespace) }
+
+ it 'returns complete path to the project' do
+ expect(project.to_reference(another_project)).to eq 'sample-project'
+ end
+ end
+ end
+
+ describe '#to_human_reference' do
+ let(:owner) { create(:user, name: 'Gitlab') }
+ let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
+ let(:project) { create(:empty_project, name: 'Sample project', namespace: namespace) }
+
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference).to be_nil
+ end
+ end
+
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference(project)).to be_nil
+ end
+ end
+
+ context 'when cross namespace project argument' do
+ let(:another_namespace_project) { create(:empty_project, name: 'another-project') }
+
+ it 'returns complete name with namespace of the project' do
+ expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
+ end
+ end
+
+ context 'when same namespace / cross-project argument' do
+ let(:another_project) { create(:empty_project, namespace: namespace) }
+
+ it 'returns name of the project' do
+ expect(project.to_human_reference(another_project)).to eq 'Sample project'
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index b797d19161d..b5a42edd192 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -768,7 +768,6 @@ describe Repository, models: true do
expect(repository).not_to receive(:expire_root_ref_cache)
expect(repository).not_to receive(:expire_emptiness_caches)
expect(repository).to receive(:expire_branches_cache)
- expect(repository).to receive(:expire_has_visible_content_cache)
repository.update_branch_with_hooks(user, 'new-feature') { new_rev }
end
@@ -786,7 +785,6 @@ describe Repository, models: true do
expect(empty_repository).to receive(:expire_root_ref_cache)
expect(empty_repository).to receive(:expire_emptiness_caches)
expect(empty_repository).to receive(:expire_branches_cache)
- expect(empty_repository).to receive(:expire_has_visible_content_cache)
empty_repository.commit_file(user, 'CHANGELOG', 'Changelog!',
'Updates file content', 'master', false)
@@ -829,15 +827,6 @@ describe Repository, models: true do
expect(subject).to eq(true)
end
-
- it 'caches the output' do
- expect(repository).to receive(:branch_count).
- once.
- and_return(3)
-
- repository.has_visible_content?
- repository.has_visible_content?
- end
end
end
@@ -918,20 +907,6 @@ describe Repository, models: true do
end
end
- describe '#expire_has_visible_content_cache' do
- it 'expires the visible content cache' do
- repository.has_visible_content?
-
- expect(repository).to receive(:branch_count).
- once.
- and_return(0)
-
- repository.expire_has_visible_content_cache
-
- expect(repository.has_visible_content?).to eq(false)
- end
- end
-
describe '#expire_branch_cache' do
# This method is private but we need it for testing purposes. Sadly there's
# no other proper way of testing caching operations.
@@ -967,7 +942,6 @@ describe Repository, models: true do
allow(repository).to receive(:empty?).and_return(true)
expect(cache).to receive(:expire).with(:empty?)
- expect(repository).to receive(:expire_has_visible_content_cache)
repository.expire_emptiness_caches
end
@@ -976,7 +950,6 @@ describe Repository, models: true do
allow(repository).to receive(:empty?).and_return(false)
expect(cache).not_to receive(:expire).with(:empty?)
- expect(repository).not_to receive(:expire_has_visible_content_cache)
repository.expire_emptiness_caches
end
@@ -1203,16 +1176,16 @@ describe Repository, models: true do
end
describe '#after_create_branch' do
- it 'flushes the visible content cache' do
- expect(repository).to receive(:expire_has_visible_content_cache)
+ it 'expires the branch caches' do
+ expect(repository).to receive(:expire_branches_cache)
repository.after_create_branch
end
end
describe '#after_remove_branch' do
- it 'flushes the visible content cache' do
- expect(repository).to receive(:expire_has_visible_content_cache)
+ it 'expires the branch caches' do
+ expect(repository).to receive(:expire_branches_cache)
repository.after_remove_branch
end
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index f62f6bacbaa..7425a897769 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -23,9 +23,9 @@ describe Snippet, models: true do
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to validate_length_of(:title).is_within(0..255) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
- it { is_expected.to validate_length_of(:file_name).is_within(0..255) }
+ it { is_expected.to validate_length_of(:file_name).is_at_most(255) }
it { is_expected.to validate_presence_of(:content) }
@@ -33,16 +33,51 @@ describe Snippet, models: true do
end
describe '#to_reference' do
+ context 'when snippet belongs to a project' do
+ let(:project) { build(:empty_project, name: 'sample-project') }
+ let(:snippet) { build(:snippet, id: 1, project: project) }
+
+ it 'returns a String reference to the object' do
+ expect(snippet.to_reference).to eq "$1"
+ end
+
+ it 'supports a cross-project reference' do
+ another_project = build(:project, name: 'another-project', namespace: project.namespace)
+ expect(snippet.to_reference(another_project)).to eq "sample-project$1"
+ end
+ end
+
+ context 'when snippet does not belong to a project' do
+ let(:snippet) { build(:snippet, id: 1, project: nil) }
+
+ it 'returns a String reference to the object' do
+ expect(snippet.to_reference).to eq "$1"
+ end
+
+ it 'still returns shortest reference when project arg present' do
+ another_project = build(:project, name: 'another-project')
+ expect(snippet.to_reference(another_project)).to eq "$1"
+ end
+ end
+ end
+
+ describe '#file_name' do
let(:project) { create(:empty_project) }
- let(:snippet) { create(:snippet, project: project) }
- it 'returns a String reference to the object' do
- expect(snippet.to_reference).to eq "$#{snippet.id}"
+ context 'file_name is nil' do
+ let(:snippet) { create(:snippet, project: project, file_name: nil) }
+
+ it 'returns an empty string' do
+ expect(snippet.file_name).to eq ''
+ end
end
- it 'supports a cross-project reference' do
- cross = double('project')
- expect(snippet.to_reference(cross)).to eq "#{project.to_reference}$#{snippet.id}"
+ context 'file_name is not nil' do
+ let(:snippet) { create(:snippet, project: project, file_name: 'foo.txt') }
+
+ it 'returns the file_name' do
+ expect(snippet.file_name).to eq 'foo.txt'
+ end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 14c891994d0..bad6ed9e146 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -79,7 +79,7 @@ describe User, models: true do
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
- it { is_expected.to validate_length_of(:bio).is_within(0..255) }
+ it { is_expected.to validate_length_of(:bio).is_at_most(255) }
it_behaves_like 'an object with email-formated attributes', :email do
subject { build(:user) }
@@ -575,6 +575,23 @@ describe User, models: true do
end
end
end
+
+ describe '#require_ssh_key?' do
+ protocol_and_expectation = {
+ 'http' => false,
+ 'ssh' => true,
+ '' => true,
+ }
+
+ protocol_and_expectation.each do |protocol, expected|
+ it "has correct require_ssh_key?" do
+ stub_application_setting(enabled_git_access_protocol: protocol)
+ user = build(:user)
+
+ expect(user.require_ssh_key?).to eq(expected)
+ end
+ end
+ end
end
describe '.find_by_any_email' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 96249a7d8c3..eeab9827d99 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -23,7 +23,7 @@ describe ProjectPolicy, models: true do
:download_code, :fork_project, :create_project_snippet, :update_issue,
:admin_issue, :admin_label, :admin_list, :read_commit_status, :read_build,
:read_container_image, :read_pipeline, :read_environment, :read_deployment,
- :read_merge_request
+ :read_merge_request, :download_wiki_code
]
end
@@ -56,7 +56,8 @@ describe ProjectPolicy, models: true do
let(:public_permissions) do
[
:download_code, :fork_project, :read_commit_status, :read_pipeline,
- :read_container_image, :build_download_code, :build_read_container_image
+ :read_container_image, :build_download_code, :build_read_container_image,
+ :download_wiki_code
]
end
@@ -87,6 +88,15 @@ describe ProjectPolicy, models: true do
expect(Ability.allowed?(user, :read_issue, project)).to be_falsy
end
+ it 'does not include the wiki permissions when the feature is disabled' do
+ project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
+ wiki_permissions = [:read_wiki, :create_wiki, :update_wiki, :admin_wiki, :download_wiki_code]
+
+ permissions = described_class.abilities(owner, project).to_set
+
+ expect(permissions).not_to include(*wiki_permissions)
+ end
+
context 'abilities for non-public projects' do
let(:project) { create(:empty_project, namespace: owner.namespace) }
@@ -101,14 +111,36 @@ describe ProjectPolicy, models: true do
context 'guests' do
let(:current_user) { guest }
+ let(:reporter_public_build_permissions) do
+ reporter_permissions - [:read_build, :read_pipeline]
+ end
+
it do
is_expected.to include(*guest_permissions)
- is_expected.not_to include(*reporter_permissions)
+ is_expected.not_to include(*reporter_public_build_permissions)
is_expected.not_to include(*team_member_reporter_permissions)
is_expected.not_to include(*developer_permissions)
is_expected.not_to include(*master_permissions)
is_expected.not_to include(*owner_permissions)
end
+
+ context 'public builds enabled' do
+ it do
+ is_expected.to include(*guest_permissions)
+ is_expected.to include(:read_build, :read_pipeline)
+ end
+ end
+
+ context 'public builds disabled' do
+ before do
+ project.update(public_builds: false)
+ end
+
+ it do
+ is_expected.to include(*guest_permissions)
+ is_expected.not_to include(:read_build, :read_pipeline)
+ end
+ end
end
context 'reporter' do
diff --git a/spec/rake_helper.rb b/spec/rake_helper.rb
index 9b5b4bf9fea..298a520f5ca 100644
--- a/spec/rake_helper.rb
+++ b/spec/rake_helper.rb
@@ -8,7 +8,7 @@ RSpec.configure do |config|
config.before(:all) do
$stdout = StringIO.new
- Rake.application.rake_require 'tasks/gitlab/task_helpers'
+ Rake.application.rake_require 'tasks/gitlab/helpers'
Rake::Task.define_task :environment
end
diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb
index 01bb9e955e0..3f34309f419 100644
--- a/spec/requests/api/api_helpers_spec.rb
+++ b/spec/requests/api/api_helpers_spec.rb
@@ -47,7 +47,7 @@ describe API::Helpers, api: true do
end
def error!(message, status)
- raise Exception
+ raise Exception.new("#{status} - #{message}")
end
describe ".current_user" do
@@ -153,85 +153,144 @@ describe API::Helpers, api: true do
end
end
- it "changes current user to sudo when admin" do
- set_env(admin, user.id)
- expect(current_user).to eq(user)
- set_param(admin, user.id)
- expect(current_user).to eq(user)
- set_env(admin, user.username)
- expect(current_user).to eq(user)
- set_param(admin, user.username)
- expect(current_user).to eq(user)
- end
+ context 'sudo usage' do
+ context 'with admin' do
+ context 'with header' do
+ context 'with id' do
+ it 'changes current_user to sudo' do
+ set_env(admin, user.id)
- it "throws an error when the current user is not an admin and attempting to sudo" do
- set_env(user, admin.id)
- expect { current_user }.to raise_error(Exception)
- set_param(user, admin.id)
- expect { current_user }.to raise_error(Exception)
- set_env(user, admin.username)
- expect { current_user }.to raise_error(Exception)
- set_param(user, admin.username)
- expect { current_user }.to raise_error(Exception)
- end
+ expect(current_user).to eq(user)
+ end
- it "throws an error when the user cannot be found for a given id" do
- id = user.id + admin.id
- expect(user.id).not_to eq(id)
- expect(admin.id).not_to eq(id)
- set_env(admin, id)
- expect { current_user }.to raise_error(Exception)
+ it 'handles sudo to oneself' do
+ set_env(admin, admin.id)
- set_param(admin, id)
- expect { current_user }.to raise_error(Exception)
- end
+ expect(current_user).to eq(admin)
+ end
- it "throws an error when the user cannot be found for a given username" do
- username = "#{user.username}#{admin.username}"
- expect(user.username).not_to eq(username)
- expect(admin.username).not_to eq(username)
- set_env(admin, username)
- expect { current_user }.to raise_error(Exception)
+ it 'throws an error when user cannot be found' do
+ id = user.id + admin.id
+ expect(user.id).not_to eq(id)
+ expect(admin.id).not_to eq(id)
- set_param(admin, username)
- expect { current_user }.to raise_error(Exception)
- end
+ set_env(admin, id)
- it "handles sudo's to oneself" do
- set_env(admin, admin.id)
- expect(current_user).to eq(admin)
- set_param(admin, admin.id)
- expect(current_user).to eq(admin)
- set_env(admin, admin.username)
- expect(current_user).to eq(admin)
- set_param(admin, admin.username)
- expect(current_user).to eq(admin)
- end
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
- it "handles multiple sudo's to oneself" do
- set_env(admin, user.id)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
- set_env(admin, user.username)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
-
- set_param(admin, user.id)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
- set_param(admin, user.username)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
- end
+ context 'with username' do
+ it 'changes current_user to sudo' do
+ set_env(admin, user.username)
+
+ expect(current_user).to eq(user)
+ end
+
+ it 'handles sudo to oneself' do
+ set_env(admin, admin.username)
+
+ expect(current_user).to eq(admin)
+ end
+
+ it "throws an error when the user cannot be found for a given username" do
+ username = "#{user.username}#{admin.username}"
+ expect(user.username).not_to eq(username)
+ expect(admin.username).not_to eq(username)
+
+ set_env(admin, username)
+
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
+ end
+
+ context 'with param' do
+ context 'with id' do
+ it 'changes current_user to sudo' do
+ set_param(admin, user.id)
+
+ expect(current_user).to eq(user)
+ end
+
+ it 'handles sudo to oneself' do
+ set_param(admin, admin.id)
+
+ expect(current_user).to eq(admin)
+ end
+
+ it 'handles sudo to oneself using string' do
+ set_env(admin, user.id.to_s)
+
+ expect(current_user).to eq(user)
+ end
+
+ it 'throws an error when user cannot be found' do
+ id = user.id + admin.id
+ expect(user.id).not_to eq(id)
+ expect(admin.id).not_to eq(id)
+
+ set_param(admin, id)
+
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
+
+ context 'with username' do
+ it 'changes current_user to sudo' do
+ set_param(admin, user.username)
+
+ expect(current_user).to eq(user)
+ end
+
+ it 'handles sudo to oneself' do
+ set_param(admin, admin.username)
+
+ expect(current_user).to eq(admin)
+ end
+
+ it "throws an error when the user cannot be found for a given username" do
+ username = "#{user.username}#{admin.username}"
+ expect(user.username).not_to eq(username)
+ expect(admin.username).not_to eq(username)
+
+ set_param(admin, username)
+
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
+ end
+ end
+
+ context 'with regular user' do
+ context 'with env' do
+ it 'changes current_user to sudo when admin and user id' do
+ set_env(user, admin.id)
+
+ expect { current_user }.to raise_error(Exception)
+ end
- it "handles multiple sudo's to oneself using string ids" do
- set_env(admin, user.id.to_s)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
+ it 'changes current_user to sudo when admin and user username' do
+ set_env(user, admin.username)
- set_param(admin, user.id.to_s)
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
+
+ context 'with params' do
+ it 'changes current_user to sudo when admin and user id' do
+ set_param(user, admin.id)
+
+ expect { current_user }.to raise_error(Exception)
+ end
+
+ it 'changes current_user to sudo when admin and user username' do
+ set_param(user, admin.username)
+
+ expect { current_user }.to raise_error(Exception)
+ end
+ end
+ end
end
end
@@ -290,4 +349,56 @@ describe API::Helpers, api: true do
handle_api_exception(exception)
end
end
+
+ describe '.authenticate_non_get!' do
+ %w[HEAD GET].each do |method_name|
+ context "method is #{method_name}" do
+ before do
+ expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name))
+ end
+
+ it 'does not raise an error' do
+ expect_any_instance_of(self.class).not_to receive(:authenticate!)
+
+ expect { authenticate_non_get! }.not_to raise_error
+ end
+ end
+ end
+
+ %w[POST PUT PATCH DELETE].each do |method_name|
+ context "method is #{method_name}" do
+ before do
+ expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name))
+ end
+
+ it 'calls authenticate!' do
+ expect_any_instance_of(self.class).to receive(:authenticate!)
+
+ authenticate_non_get!
+ end
+ end
+ end
+ end
+
+ describe '.authenticate!' do
+ context 'current_user is nil' do
+ before do
+ expect_any_instance_of(self.class).to receive(:current_user).and_return(nil)
+ end
+
+ it 'returns a 401 response' do
+ expect { authenticate! }.to raise_error '401 - {"message"=>"401 Unauthorized"}'
+ end
+ end
+
+ context 'current_user is present' do
+ before do
+ expect_any_instance_of(self.class).to receive(:current_user).and_return(true)
+ end
+
+ it 'does not raise an error' do
+ expect { authenticate! }.not_to raise_error
+ end
+ end
+ end
end
diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb
index 5ad4fc4865a..c8e8f31cc1f 100644
--- a/spec/requests/api/award_emoji_spec.rb
+++ b/spec/requests/api/award_emoji_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::AwardEmoji, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:empty_project) }
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index 4f5c09a3029..3019724f52e 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Boards, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index fe6b875b997..55b8c8c0c69 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'mime/types'
-describe API::API, api: true do
+describe API::Branches, api: true do
include ApiHelpers
let(:user) { create(:user) }
@@ -31,11 +31,22 @@ describe API::API, api: true do
expect(json_response['name']).to eq(branch_name)
expect(json_response['commit']['id']).to eq(branch_sha)
+ expect(json_response['merged']).to eq(false)
expect(json_response['protected']).to eq(false)
expect(json_response['developers_can_push']).to eq(false)
expect(json_response['developers_can_merge']).to eq(false)
end
+ context 'on a merged branch' do
+ it "returns the branch information for a single branch" do
+ get api("/projects/#{project.id}/repository/branches/merge-test", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq('merge-test')
+ expect(json_response['merged']).to eq(true)
+ end
+ end
+
it "returns a 403 error if guest" do
get api("/projects/#{project.id}/repository/branches", user2)
expect(response).to have_http_status(403)
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index fc72a44d663..7be7acebb19 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Builds, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:api_user) { user }
- let!(:project) { create(:project, creator_id: user.id) }
+ let!(:project) { create(:project, creator_id: user.id, public_builds: false) }
let!(:developer) { create(:project_member, :developer, user: user, project: project) }
let(:reporter) { create(:project_member, :reporter, project: project) }
let(:guest) { create(:project_member, :guest, project: project) }
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index a6e8550fac3..e497bce6943 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'mime/types'
-describe API::API, api: true do
+describe API::Commits, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
@@ -18,11 +18,14 @@ describe API::API, api: true do
before { project.team << [user2, :reporter] }
it "returns project commits" do
+ commit = project.repository.commit
get api("/projects/#{project.id}/repository/commits", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_http_status(200)
expect(json_response).to be_an Array
- expect(json_response.first['id']).to eq(project.repository.commit.id)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(json_response.first['committer_name']).to eq(commit.committer_name)
+ expect(json_response.first['committer_email']).to eq(commit.committer_email)
end
end
@@ -134,6 +137,8 @@ describe API::API, api: true do
expect(response).to have_http_status(201)
expect(json_response['title']).to eq(message)
+ expect(json_response['committer_name']).to eq(user.name)
+ expect(json_response['committer_email']).to eq(user.email)
end
it 'returns a 400 bad request if file exists' do
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 65897edba7f..aabab8e6ae6 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::DeployKeys, api: true do
include ApiHelpers
let(:user) { create(:user) }
@@ -75,7 +75,6 @@ describe API::API, api: true do
expect(response).to have_http_status(400)
expect(json_response['message']['key']).to eq([
'can\'t be blank',
- 'is too short (minimum is 0 characters)',
'is invalid'
])
end
@@ -85,8 +84,7 @@ describe API::API, api: true do
expect(response).to have_http_status(400)
expect(json_response['message']['title']).to eq([
- 'can\'t be blank',
- 'is too short (minimum is 0 characters)'
+ 'can\'t be blank'
])
end
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 8fa8c66db6c..31e3cfa1b2f 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Deployments, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 1898b07835d..126496c43a5 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Environments, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 050d0dd082d..2081f80ccc1 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Files, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) }
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index d9fdafde05e..548ed8e1892 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Groups, api: true do
include ApiHelpers
let(:user1) { create(:user, can_create_group: false) }
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index e88a7e27d45..35644bd8cc9 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Internal, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index ae7994af981..5c80dd98dc7 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Issues, api: true do
include ApiHelpers
+ include EmailHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
@@ -71,13 +72,6 @@ describe API::API, api: true do
expect(json_response.last).to have_key('web_url')
end
- it "adds pagination headers and keep query params" do
- get api("/issues?state=closed&per_page=3", user)
- expect(response.headers['Link']).to eq(
- '<http://www.example.com/api/v3/issues?page=1&per_page=3&private_token=%s&state=closed>; rel="first", <http://www.example.com/api/v3/issues?page=1&per_page=3&private_token=%s&state=closed>; rel="last"' % [user.private_token, user.private_token]
- )
- end
-
it 'returns an array of closed issues' do
get api('/issues?state=closed', user)
expect(response).to have_http_status(200)
@@ -648,9 +642,8 @@ describe API::API, api: true do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', confidential: 'foo'
- expect(response).to have_http_status(201)
- expect(json_response['title']).to eq('new issue')
- expect(json_response['confidential']).to be_falsy
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('confidential is invalid')
end
it "sends notifications for subscribers of newly added labels" do
@@ -691,6 +684,32 @@ describe API::API, api: true do
])
end
+ context 'resolving issues in a merge request' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ before do
+ project.team << [user, :master]
+ post api("/projects/#{project.id}/issues", user),
+ title: 'New Issue',
+ merge_request_for_resolving_discussions: merge_request.iid
+ end
+
+ it 'creates a new project issue' do
+ expect(response).to have_http_status(:created)
+ end
+
+ it 'resolves the discussions in a merge request' do
+ discussion.first_note.reload
+
+ expect(discussion.resolved?).to be(true)
+ end
+
+ it 'assigns a description to the issue mentioning the merge request' do
+ expect(json_response['description']).to include(merge_request.to_reference)
+ end
+ end
+
context 'with due date' do
it 'creates a new project issue' do
due_date = 2.weeks.from_now.strftime('%Y-%m-%d')
@@ -835,8 +854,8 @@ describe API::API, api: true do
put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user),
confidential: 'foo'
- expect(response).to have_http_status(200)
- expect(json_response['confidential']).to be_truthy
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('confidential is invalid')
end
end
end
@@ -958,6 +977,14 @@ describe API::API, api: true do
expect(json_response['state']).to eq 'opened'
end
end
+
+ context 'when issue does not exist' do
+ it 'returns 404 when trying to move an issue' do
+ delete api("/projects/#{project.id}/issues/123", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
end
describe '/projects/:id/issues/:issue_id/move' do
@@ -1006,6 +1033,7 @@ describe API::API, api: true do
to_project_id: target_project.id
expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Issue Not Found')
end
end
@@ -1015,6 +1043,7 @@ describe API::API, api: true do
to_project_id: target_project.id
expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
end
end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index 893ed5c2b10..4c80987d680 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Keys, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index aaf41639277..b29ce1ea25e 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Labels, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index 131c2d406ea..e1887138aab 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe API::API, 'MergeRequestDiffs', api: true do
+describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
include ApiHelpers
let!(:user) { create(:user) }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 3ecf3eea5f5..0b89ac7960e 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe API::API, api: true do
+describe API::MergeRequests, api: true do
include ApiHelpers
let(:base_time) { Time.now }
let(:user) { create(:user) }
@@ -465,8 +465,8 @@ describe API::API, api: true do
expect(response).to have_http_status(200)
end
- it "enables merge when build succeeds if the ci is active" do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+ it "enables merge when pipeline succeeds if the pipeline is active" do
+ allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index b0946a838a1..8beef821d6c 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Milestones, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace ) }
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 5347cf4f7bc..c1edf384d5c 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Namespaces, api: true do
include ApiHelpers
let(:admin) { create(:admin) }
let(:user) { create(:user) }
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index b71a4c5a56e..028f93c8561 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Notes, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, :public, namespace: user.namespace) }
diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb
index e6d8a5ee954..8691a81420f 100644
--- a/spec/requests/api/notification_settings_spec.rb
+++ b/spec/requests/api/notification_settings_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::NotificationSettings, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index a7e511aaeda..9a01f7fa1c4 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Pipelines, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index 5f39329a1b8..a42cedae614 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, 'ProjectHooks', api: true do
+describe API::ProjectHooks, 'ProjectHooks', api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user3) { create(:user) }
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 1c25fd04339..01032c0929b 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe API::API, api: true do
+describe API::ProjectSnippets, api: true do
include ApiHelpers
let(:project) { create(:empty_project, :public) }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e53ee2a4e76..c5d67a90abc 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
require 'spec_helper'
-describe API::API, api: true do
+describe API::Projects, api: true do
include ApiHelpers
include Gitlab::CurrentSettings
let(:user) { create(:user) }
@@ -200,32 +200,43 @@ describe API::API, api: true do
end
describe 'GET /projects/visible' do
- let(:public_project) { create(:project, :public) }
+ shared_examples_for 'visible projects response' do
+ it 'returns the visible projects' do
+ get api('/projects/visible', current_user)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id))
+ end
+ end
+
+ let!(:public_project) { create(:project, :public) }
before do
- public_project
project
project2
project3
project4
end
- it 'returns the projects viewable by the user' do
- get api('/projects/visible', user)
-
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.map { |project| project['id'] }).
- to contain_exactly(public_project.id, project.id, project2.id, project3.id)
+ context 'when unauthenticated' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { nil }
+ let(:projects) { [public_project] }
+ end
end
- it 'shows only public projects when the user only has access to those' do
- get api('/projects/visible', user2)
+ context 'when authenticated' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { user }
+ let(:projects) { [public_project, project, project2, project3] }
+ end
+ end
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.map { |project| project['id'] }).
- to contain_exactly(public_project.id)
+ context 'when authenticated as a different user' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { user2 }
+ let(:projects) { [public_project] }
+ end
end
end
@@ -415,16 +426,7 @@ describe API::API, api: true do
not_to change { Project.count }
expect(response).to have_http_status(400)
- expect(json_response['message']['name']).to eq([
- 'can\'t be blank',
- 'is too short (minimum is 0 characters)',
- Gitlab::Regex.project_name_regex_message
- ])
- expect(json_response['message']['path']).to eq([
- 'can\'t be blank',
- 'is too short (minimum is 0 characters)',
- Gitlab::Regex.send(:project_path_regex_message)
- ])
+ expect(json_response['error']).to eq('name is missing')
end
it 'assigns attributes to project' do
@@ -438,6 +440,7 @@ describe API::API, api: true do
post api("/projects/user/#{user.id}", admin), project
+ expect(response).to have_http_status(201)
project.each_pair do |k, v|
next if %i[has_external_issue_tracker path].include?(k)
expect(json_response[k.to_s]).to eq(v)
@@ -447,6 +450,8 @@ describe API::API, api: true do
it 'sets a project as public' do
project = attributes_for(:project, :public)
post api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
expect(json_response['public']).to be_truthy
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
@@ -454,6 +459,8 @@ describe API::API, api: true do
it 'sets a project as public using :public' do
project = attributes_for(:project, { public: true })
post api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
expect(json_response['public']).to be_truthy
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
@@ -461,6 +468,8 @@ describe API::API, api: true do
it 'sets a project as internal' do
project = attributes_for(:project, :internal)
post api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
@@ -468,6 +477,7 @@ describe API::API, api: true do
it 'sets a project as internal overriding :public' do
project = attributes_for(:project, :internal, { public: true })
post api("/projects/user/#{user.id}", admin), project
+ expect(response).to have_http_status(201)
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
@@ -529,135 +539,150 @@ describe API::API, api: true do
end
describe 'GET /projects/:id' do
- before { project }
- before { project_member }
-
- it 'returns a project by id' do
- group = create(:group)
- link = create(:project_group_link, project: project, group: group)
+ context 'when unauthenticated' do
+ it 'returns the public projects' do
+ public_project = create(:project, :public)
- get api("/projects/#{project.id}", user)
+ get api("/projects/#{public_project.id}")
- expect(response).to have_http_status(200)
- expect(json_response['id']).to eq(project.id)
- expect(json_response['description']).to eq(project.description)
- expect(json_response['default_branch']).to eq(project.default_branch)
- expect(json_response['tag_list']).to be_an Array
- expect(json_response['public']).to be_falsey
- expect(json_response['archived']).to be_falsey
- expect(json_response['visibility_level']).to be_present
- expect(json_response['ssh_url_to_repo']).to be_present
- expect(json_response['http_url_to_repo']).to be_present
- expect(json_response['web_url']).to be_present
- expect(json_response['owner']).to be_a Hash
- expect(json_response['owner']).to be_a Hash
- expect(json_response['name']).to eq(project.name)
- expect(json_response['path']).to be_present
- expect(json_response['issues_enabled']).to be_present
- expect(json_response['merge_requests_enabled']).to be_present
- expect(json_response['wiki_enabled']).to be_present
- expect(json_response['builds_enabled']).to be_present
- expect(json_response['snippets_enabled']).to be_present
- expect(json_response['container_registry_enabled']).to be_present
- expect(json_response['created_at']).to be_present
- expect(json_response['last_activity_at']).to be_present
- expect(json_response['shared_runners_enabled']).to be_present
- expect(json_response['creator_id']).to be_present
- expect(json_response['namespace']).to be_present
- expect(json_response['avatar_url']).to be_nil
- expect(json_response['star_count']).to be_present
- expect(json_response['forks_count']).to be_present
- expect(json_response['public_builds']).to be_present
- expect(json_response['shared_with_groups']).to be_an Array
- expect(json_response['shared_with_groups'].length).to eq(1)
- expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
- expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
- expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
- expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
- expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
- end
-
- it 'returns a project by path name' do
- get api("/projects/#{project.id}", user)
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(project.name)
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(public_project.id)
+ expect(json_response['description']).to eq(public_project.description)
+ expect(json_response.keys).not_to include('permissions')
+ end
end
- it 'returns a 404 error if not found' do
- get api('/projects/42', user)
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Project Not Found')
- end
+ context 'when authenticated' do
+ before do
+ project
+ project_member
+ end
- it 'returns a 404 error if user is not a member' do
- other_user = create(:user)
- get api("/projects/#{project.id}", other_user)
- expect(response).to have_http_status(404)
- end
+ it 'returns a project by id' do
+ group = create(:group)
+ link = create(:project_group_link, project: project, group: group)
- it 'handles users with dots' do
- dot_user = create(:user, username: 'dot.user')
- project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace)
+ get api("/projects/#{project.id}", user)
- get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(project.name)
- end
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(project.id)
+ expect(json_response['description']).to eq(project.description)
+ expect(json_response['default_branch']).to eq(project.default_branch)
+ expect(json_response['tag_list']).to be_an Array
+ expect(json_response['public']).to be_falsey
+ expect(json_response['archived']).to be_falsey
+ expect(json_response['visibility_level']).to be_present
+ expect(json_response['ssh_url_to_repo']).to be_present
+ expect(json_response['http_url_to_repo']).to be_present
+ expect(json_response['web_url']).to be_present
+ expect(json_response['owner']).to be_a Hash
+ expect(json_response['owner']).to be_a Hash
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to be_present
+ expect(json_response['issues_enabled']).to be_present
+ expect(json_response['merge_requests_enabled']).to be_present
+ expect(json_response['wiki_enabled']).to be_present
+ expect(json_response['builds_enabled']).to be_present
+ expect(json_response['snippets_enabled']).to be_present
+ expect(json_response['container_registry_enabled']).to be_present
+ expect(json_response['created_at']).to be_present
+ expect(json_response['last_activity_at']).to be_present
+ expect(json_response['shared_runners_enabled']).to be_present
+ expect(json_response['creator_id']).to be_present
+ expect(json_response['namespace']).to be_present
+ expect(json_response['avatar_url']).to be_nil
+ expect(json_response['star_count']).to be_present
+ expect(json_response['forks_count']).to be_present
+ expect(json_response['public_builds']).to be_present
+ expect(json_response['shared_with_groups']).to be_an Array
+ expect(json_response['shared_with_groups'].length).to eq(1)
+ expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
+ expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
+ expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
+ expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
+ end
+
+ it 'returns a project by path name' do
+ get api("/projects/#{project.id}", user)
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(project.name)
+ end
- describe 'permissions' do
- context 'all projects' do
- before { project.team << [user, :master] }
+ it 'returns a 404 error if not found' do
+ get api('/projects/42', user)
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
- it 'contains permission information' do
- get api("/projects", user)
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+ get api("/projects/#{project.id}", other_user)
+ expect(response).to have_http_status(404)
+ end
- expect(response).to have_http_status(200)
- expect(json_response.first['permissions']['project_access']['access_level']).
- to eq(Gitlab::Access::MASTER)
- expect(json_response.first['permissions']['group_access']).to be_nil
- end
+ it 'handles users with dots' do
+ dot_user = create(:user, username: 'dot.user')
+ project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace)
+
+ get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(project.name)
end
- context 'personal project' do
- it 'sets project access and returns 200' do
- project.team << [user, :master]
- get api("/projects/#{project.id}", user)
+ describe 'permissions' do
+ context 'all projects' do
+ before { project.team << [user, :master] }
- expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']['access_level']).
+ it 'contains permission information' do
+ get api("/projects", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.first['permissions']['project_access']['access_level']).
to eq(Gitlab::Access::MASTER)
- expect(json_response['permissions']['group_access']).to be_nil
+ expect(json_response.first['permissions']['group_access']).to be_nil
+ end
end
- end
- context 'group project' do
- let(:project2) { create(:project, group: create(:group)) }
+ context 'personal project' do
+ it 'sets project access and returns 200' do
+ project.team << [user, :master]
+ get api("/projects/#{project.id}", user)
- before { project2.group.add_owner(user) }
+ expect(response).to have_http_status(200)
+ expect(json_response['permissions']['project_access']['access_level']).
+ to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['group_access']).to be_nil
+ end
+ end
- it 'sets the owner and return 200' do
- get api("/projects/#{project2.id}", user)
+ context 'group project' do
+ let(:project2) { create(:project, group: create(:group)) }
- expect(response).to have_http_status(200)
- expect(json_response['permissions']['project_access']).to be_nil
- expect(json_response['permissions']['group_access']['access_level']).
+ before { project2.group.add_owner(user) }
+
+ it 'sets the owner and return 200' do
+ get api("/projects/#{project2.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['permissions']['project_access']).to be_nil
+ expect(json_response['permissions']['group_access']['access_level']).
to eq(Gitlab::Access::OWNER)
+ end
end
end
end
end
describe 'GET /projects/:id/events' do
- before { project_member2 }
-
- context 'valid request' do
- before do
+ shared_examples_for 'project events response' do
+ it 'returns the project events' do
+ member = create(:user)
+ create(:project_member, :developer, user: member, project: project)
note = create(:note_on_issue, note: 'What an awesome day!', project: project)
EventCreateService.new.leave_note(note, note.author)
- end
- it 'returns all events' do
- get api("/projects/#{project.id}/events", user)
+ get api("/projects/#{project.id}/events", current_user)
expect(response).to have_http_status(200)
@@ -670,24 +695,90 @@ describe API::API, api: true do
expect(last_event['action_name']).to eq('joined')
expect(last_event['project_id'].to_i).to eq(project.id)
- expect(last_event['author_username']).to eq(user3.username)
- expect(last_event['author']['name']).to eq(user3.name)
+ expect(last_event['author_username']).to eq(member.username)
+ expect(last_event['author']['name']).to eq(member.name)
end
end
- it 'returns a 404 error if not found' do
- get api('/projects/42/events', user)
+ context 'when unauthenticated' do
+ it_behaves_like 'project events response' do
+ let(:project) { create(:project, :public) }
+ let(:current_user) { nil }
+ end
+ end
- expect(response).to have_http_status(404)
- expect(json_response['message']).to eq('404 Project Not Found')
+ context 'when authenticated' do
+ context 'valid request' do
+ it_behaves_like 'project events response' do
+ let(:current_user) { user }
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get api('/projects/42/events', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+
+ get api("/projects/#{project.id}/events", other_user)
+
+ expect(response).to have_http_status(404)
+ end
end
+ end
- it 'returns a 404 error if user is not a member' do
- other_user = create(:user)
+ describe 'GET /projects/:id/users' do
+ shared_examples_for 'project users response' do
+ it 'returns the project users' do
+ member = create(:user)
+ create(:project_member, :developer, user: member, project: project)
- get api("/projects/#{project.id}/events", other_user)
+ get api("/projects/#{project.id}/users", current_user)
- expect(response).to have_http_status(404)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+
+ first_user = json_response.first
+
+ expect(first_user['username']).to eq(member.username)
+ expect(first_user['name']).to eq(member.name)
+ expect(first_user.keys).to contain_exactly(*%w[name username id state avatar_url web_url])
+ end
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'project users response' do
+ let(:project) { create(:project, :public) }
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when authenticated' do
+ context 'valid request' do
+ it_behaves_like 'project users response' do
+ let(:current_user) { user }
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get api('/projects/42/users', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+
+ get api("/projects/#{project.id}/users", other_user)
+
+ expect(response).to have_http_status(404)
+ end
end
end
@@ -848,7 +939,7 @@ describe API::API, api: true do
it 'is idempotent if not forked' do
expect(project_fork_target.forked_from_project).to be_nil
delete api("/projects/#{project_fork_target.id}/fork", admin)
- expect(response).to have_http_status(200)
+ expect(response).to have_http_status(304)
expect(project_fork_target.reload.forked_from_project).to be_nil
end
end
@@ -865,7 +956,7 @@ describe API::API, api: true do
post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at
end.to change { ProjectGroupLink.count }.by(1)
- expect(response.status).to eq 201
+ expect(response).to have_http_status(201)
expect(json_response['group_id']).to eq(group.id)
expect(json_response['group_access']).to eq(Gitlab::Access::DEVELOPER)
expect(json_response['expires_at']).to eq(expires_at.to_s)
@@ -873,18 +964,18 @@ describe API::API, api: true do
it "returns a 400 error when group id is not given" do
post api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
- expect(response.status).to eq 400
+ expect(response).to have_http_status(400)
end
it "returns a 400 error when access level is not given" do
post api("/projects/#{project.id}/share", user), group_id: group.id
- expect(response.status).to eq 400
+ expect(response).to have_http_status(400)
end
it "returns a 400 error when sharing is disabled" do
project.namespace.update(share_with_group_lock: true)
post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
- expect(response.status).to eq 400
+ expect(response).to have_http_status(400)
end
it 'returns a 404 error when user cannot read group' do
@@ -892,19 +983,20 @@ describe API::API, api: true do
post api("/projects/#{project.id}/share", user), group_id: private_group.id, group_access: Gitlab::Access::DEVELOPER
- expect(response.status).to eq 404
+ expect(response).to have_http_status(404)
end
it 'returns a 404 error when group does not exist' do
post api("/projects/#{project.id}/share", user), group_id: 1234, group_access: Gitlab::Access::DEVELOPER
- expect(response.status).to eq 404
+ expect(response).to have_http_status(404)
end
- it "returns a 409 error when wrong params passed" do
+ it "returns a 400 error when wrong params passed" do
post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
- expect(response.status).to eq 409
- expect(json_response['message']).to eq 'Group access is not included in the list'
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq 'group_access does not have a valid value'
end
end
@@ -950,35 +1042,37 @@ describe API::API, api: true do
let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
+ shared_examples_for 'project search response' do |args = {}|
+ it 'returns project search responses' do
+ get api("/projects/search/#{query}", current_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(args[:results])
+ json_response.each { |project| expect(project['name']).to match(args[:match_regex] || /.*query.*/) }
+ end
+ end
+
context 'when unauthenticated' do
- it 'returns authentication error' do
- get api("/projects/search/#{query}")
- expect(response).to have_http_status(401)
+ it_behaves_like 'project search response', results: 1 do
+ let(:current_user) { nil }
end
end
context 'when authenticated' do
- it 'returns an array of projects' do
- get api("/projects/search/#{query}", user)
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.size).to eq(6)
- json_response.each {|project| expect(project['name']).to match(/.*query.*/)}
+ it_behaves_like 'project search response', results: 6 do
+ let(:current_user) { user }
end
end
context 'when authenticated as a different user' do
- it 'returns matching public projects' do
- get api("/projects/search/#{query}", user2)
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.size).to eq(2)
- json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)}
+ it_behaves_like 'project search response', results: 2, match_regex: /(internal|public) query/ do
+ let(:current_user) { user2 }
end
end
end
- describe 'PUT /projects/:id̈́' do
+ describe 'PUT /projects/:id' do
before { project }
before { user }
before { user3 }
@@ -1017,7 +1111,6 @@ describe API::API, api: true do
it 'updates visibility_level from public to private' do
project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
-
project_param = { public: false }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 38c8ad34f9d..c90b69e8ebb 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'mime/types'
-describe API::API, api: true do
+describe API::Repositories, api: true do
include ApiHelpers
include RepoHelpers
include WorkhorseHelpers
@@ -44,7 +44,6 @@ describe API::API, api: true do
end
end
-
describe 'GET /projects/:id/repository/tree?recursive=1' do
context 'authorized user' do
before { project.team << [user2, :reporter] }
@@ -67,7 +66,7 @@ describe API::API, api: true do
expect(json_response).to be_an Object
json_response['message'] == '404 Tree Not Found'
- end
+ end
end
context "unauthorized user" do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index bb0344e5995..d30361f53d4 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe API::API, api: true do
+describe API::Services, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:admin) { create(:admin) }
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
index e3f22b4c578..794e2b5c04d 100644
--- a/spec/requests/api/session_spec.rb
+++ b/spec/requests/api/session_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Session, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 096a8ebab70..9a8d633d657 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, 'Settings', api: true do
+describe API::Settings, 'Settings', api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 6c9df21f598..b3e5afdadb1 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::SystemHooks, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index d563883cd47..a1c32ae65ba 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require 'mime/types'
-describe API::API, api: true do
+describe API::Tags, api: true do
include ApiHelpers
include RepoHelpers
@@ -15,6 +15,31 @@ describe API::API, api: true do
let(:tag_name) { project.repository.tag_names.sort.reverse.first }
let(:description) { 'Awesome release!' }
+ shared_examples_for 'repository tags' do
+ it 'returns the repository tags' do
+ get api("/projects/#{project.id}/repository/tags", current_user)
+
+ expect(response).to have_http_status(200)
+
+ first_tag = json_response.first
+
+ expect(first_tag['name']).to eq(tag_name)
+ end
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'repository tags' do
+ let(:project) { create(:project, :public) }
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when authenticated' do
+ it_behaves_like 'repository tags' do
+ let(:current_user) { user }
+ end
+ end
+
context 'without releases' do
it "returns an array of project tags" do
get api("/projects/#{project.id}/repository/tags", user)
@@ -45,17 +70,33 @@ describe API::API, api: true do
describe 'GET /projects/:id/repository/tags/:tag_name' do
let(:tag_name) { project.repository.tag_names.sort.reverse.first }
- it 'returns a specific tag' do
- get api("/projects/#{project.id}/repository/tags/#{tag_name}", user)
+ shared_examples_for 'repository tag' do
+ it 'returns the repository tag' do
+ get api("/projects/#{project.id}/repository/tags/#{tag_name}", current_user)
+
+ expect(response).to have_http_status(200)
+
+ expect(json_response['name']).to eq(tag_name)
+ end
+
+ it 'returns 404 for an invalid tag name' do
+ get api("/projects/#{project.id}/repository/tags/foobar", current_user)
- expect(response).to have_http_status(200)
- expect(json_response['name']).to eq(tag_name)
+ expect(response).to have_http_status(404)
+ end
end
- it 'returns 404 for an invalid tag name' do
- get api("/projects/#{project.id}/repository/tags/foobar", user)
+ context 'when unauthenticated' do
+ it_behaves_like 'repository tag' do
+ let(:project) { create(:project, :public) }
+ let(:current_user) { nil }
+ end
+ end
- expect(response).to have_http_status(404)
+ context 'when authenticated' do
+ it_behaves_like 'repository tag' do
+ let(:current_user) { user }
+ end
end
end
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index c890a51ae42..67ec3168679 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API do
+describe API::Triggers do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 1a6e7716b2f..c37dbfa0a33 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Users, api: true do
include ApiHelpers
let(:user) { create(:user) }
@@ -651,20 +651,75 @@ describe API::API, api: true do
end
describe "GET /user" do
- it "returns current user" do
- get api("/user", user)
- expect(response).to have_http_status(200)
- expect(json_response['email']).to eq(user.email)
- expect(json_response['is_admin']).to eq(user.is_admin?)
- expect(json_response['can_create_project']).to eq(user.can_create_project?)
- expect(json_response['can_create_group']).to eq(user.can_create_group?)
- expect(json_response['projects_limit']).to eq(user.projects_limit)
- expect(json_response['private_token']).to be_blank
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:private_token) { user.private_token }
+
+ context 'with regular user' do
+ context 'with personal access token' do
+ it 'returns 403 without private token when sudo is defined' do
+ get api("/user?private_token=#{personal_access_token.token}&sudo=#{user.id}")
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'with private token' do
+ it 'returns 403 without private token when sudo defined' do
+ get api("/user?private_token=#{private_token}&sudo=#{user.id}")
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ it 'returns current user without private token when sudo not defined' do
+ get api("/user", user)
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('user/public')
+ end
end
- it "returns 401 error if user is unauthenticated" do
- get api("/user")
- expect(response).to have_http_status(401)
+ context 'with admin' do
+ let(:user) { create(:admin) }
+
+ context 'with personal access token' do
+ it 'returns 403 without private token when sudo defined' do
+ get api("/user?private_token=#{personal_access_token.token}&sudo=#{user.id}")
+
+ expect(response).to have_http_status(403)
+ end
+
+ it 'returns current user without private token when sudo not defined' do
+ get api("/user?private_token=#{personal_access_token.token}")
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('user/public')
+ end
+ end
+
+ context 'with private token' do
+ it 'returns current user with private token when sudo defined' do
+ get api("/user?private_token=#{private_token}&sudo=#{user.id}")
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('user/login')
+ end
+
+ it 'returns current user without private token when sudo not defined' do
+ get api("/user?private_token=#{private_token}")
+
+ expect(response).to have_http_status(200)
+ expect(response).to match_response_schema('user/public')
+ end
+ end
+ end
+
+ context 'with unauthenticated user' do
+ it "returns 401 error if user is unauthenticated" do
+ get api("/user")
+
+ expect(response).to have_http_status(401)
+ end
end
end
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 05fbdb909dc..7435f320607 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Variables, api: true do
include ApiHelpers
let(:user) { create(:user) }
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 54b69a0cae7..da1b2fda70e 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::API, api: true do
+describe API::Version, api: true do
include ApiHelpers
describe 'GET /version' do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index a09d8689ff2..80652129928 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::API::API do
+describe Ci::API::Builds do
include ApiHelpers
let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) }
diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb
index d6c26fd8a94..bd55934d0c8 100644
--- a/spec/requests/ci/api/runners_spec.rb
+++ b/spec/requests/ci/api/runners_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::API::API do
+describe Ci::API::Runners do
include ApiHelpers
include StubGitlabCalls
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 0a0f979f57d..2d434ab5dd8 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Ci::API::API do
+describe Ci::API::Triggers do
include ApiHelpers
describe 'POST /projects/:project_id/refs/:ref/trigger' do
diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb
index 705dbb7d1c0..e0368e6001f 100644
--- a/spec/requests/projects/cycle_analytics_events_spec.rb
+++ b/spec/requests/projects/cycle_analytics_events_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe 'cycle analytics events' do
let(:user) { create(:user) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, public_builds: false) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
describe 'GET /:namespace/:project/cycle_analytics/events/issues' do
@@ -40,7 +40,7 @@ describe 'cycle analytics events' do
expect(json_response['events']).not_to be_empty
- first_mr_iid = MergeRequest.order(created_at: :desc).pluck(:iid).first.to_s
+ first_mr_iid = project.merge_requests.order(id: :desc).pluck(:iid).first.to_s
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
@@ -135,6 +135,6 @@ describe 'cycle analytics events' do
merge_merge_requests_closing_issue(issue)
- ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha)
+ ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
end
end
diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb
index c0b7e86b17c..6b33fe66a63 100644
--- a/spec/serializers/analytics_build_entity_spec.rb
+++ b/spec/serializers/analytics_build_entity_spec.rb
@@ -7,7 +7,9 @@ describe AnalyticsBuildEntity do
context 'build with an author' do
let(:user) { create(:user) }
- let(:build) { create(:ci_build, author: user, started_at: 2.hours.ago, finished_at: 1.hour.ago) }
+ let(:started_at) { 2.hours.ago }
+ let(:finished_at) { 1.hour.ago }
+ let(:build) { create(:ci_build, author: user, started_at: started_at, finished_at: finished_at) }
subject { entity.as_json }
@@ -31,5 +33,54 @@ describe AnalyticsBuildEntity do
it 'contains the duration' do
expect(subject[:total_time]).to eq(hours: 1 )
end
+
+ context 'no started at or finished at date' do
+ let(:started_at) { nil }
+ let(:finished_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('Not started')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({})
+ end
+ end
+
+ context 'no started at date' do
+ let(:started_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('Not started')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({})
+ end
+ end
+
+ context 'no finished at date' do
+ let(:finished_at) { nil }
+
+ it 'does not blow up' do
+ expect{ subject[:date] }.not_to raise_error
+ end
+
+ it 'shows the right message' do
+ expect(subject[:date]).to eq('about 2 hours ago')
+ end
+
+ it 'shows the right total time' do
+ expect(subject[:total_time]).to eq({ hours: 2 })
+ end
+ end
end
end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index ff113efd916..ebb11166964 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -1,31 +1,10 @@
require 'spec_helper'
describe Ci::ProcessPipelineService, services: true do
- let(:pipeline) { create(:ci_pipeline, ref: 'master') }
+ let(:pipeline) { create(:ci_empty_pipeline, ref: 'master') }
let(:user) { create(:user) }
- let(:config) { nil }
-
- before do
- allow(pipeline).to receive(:ci_yaml_file).and_return(config)
- end
describe '#execute' do
- def all_builds
- pipeline.builds
- end
-
- def builds
- all_builds.where.not(status: [:created, :skipped])
- end
-
- def process_pipeline
- described_class.new(pipeline.project, user).execute(pipeline)
- end
-
- def succeed_pending
- builds.pending.update_all(status: 'success')
- end
-
context 'start queuing next builds' do
before do
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage_idx: 0)
@@ -223,10 +202,6 @@ describe Ci::ProcessPipelineService, services: true do
pipeline.builds.running_or_pending.each(&:success)
expect(manual_actions).to be_many # production and clear cache
end
-
- def manual_actions
- pipeline.manual_actions
- end
end
end
@@ -282,15 +257,6 @@ describe Ci::ProcessPipelineService, services: true do
expect(builds.map(&:status)).to eq(%w[success skipped pending])
end
end
-
- def create_build(name, stage_idx, when_value = nil)
- create(:ci_build,
- :created,
- pipeline: pipeline,
- name: name,
- stage_idx: stage_idx,
- when: when_value)
- end
end
context 'when failed build in the middle stage is retried' do
@@ -327,65 +293,92 @@ describe Ci::ProcessPipelineService, services: true do
end
end
- context 'creates a builds from .gitlab-ci.yml' do
- let(:config) do
- YAML.dump({
- rspec: {
- stage: 'test',
- script: 'rspec'
- },
- rubocop: {
- stage: 'test',
- script: 'rubocop'
- },
- deploy: {
- stage: 'deploy',
- script: 'deploy'
- }
- })
+ context 'when there are builds that are not created yet' do
+ let(:pipeline) do
+ create(:ci_pipeline, config: config)
end
- # Using stubbed .gitlab-ci.yml created in commit factory
- #
+ let(:config) do
+ { rspec: { stage: 'test', script: 'rspec' },
+ deploy: { stage: 'deploy', script: 'rsync' } }
+ end
before do
- stub_ci_pipeline_yaml_file(config)
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage: 'build', stage_idx: 0)
create(:ci_build, :created, pipeline: pipeline, name: 'mac', stage: 'build', stage_idx: 0)
end
- it 'when processing a pipeline' do
- # Currently we have two builds with state created
+ it 'processes the pipeline' do
+ # Currently we have five builds with state created
+ #
expect(builds.count).to eq(0)
expect(all_builds.count).to eq(2)
- # Create builds will mark the created as pending
- expect(process_pipeline).to be_truthy
+ # Process builds service will enqueue builds from the first stage.
+ #
+ process_pipeline
+
expect(builds.count).to eq(2)
expect(all_builds.count).to eq(2)
- # When we builds succeed we will create a rest of pipeline from .gitlab-ci.yml
- # We will have 2 succeeded, 2 pending (from stage test), total 5 (one more build from deploy)
+ # When builds succeed we will enqueue remaining builds.
+ #
+ # We will have 2 succeeded, 1 pending (from stage test), total 4 (two
+ # additional build from `.gitlab-ci.yml`).
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.success.count).to eq(2)
- expect(builds.pending.count).to eq(2)
- expect(all_builds.count).to eq(5)
+ expect(builds.pending.count).to eq(1)
+ expect(all_builds.count).to eq(4)
- # When we succeed the 2 pending from stage test,
- # We will queue a deploy stage, no new builds will be created
+ # When pending build succeeds in stage test, we enqueue deploy stage.
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.pending.count).to eq(1)
- expect(builds.success.count).to eq(4)
- expect(all_builds.count).to eq(5)
+ expect(builds.success.count).to eq(3)
+ expect(all_builds.count).to eq(4)
- # When we succeed last pending build, we will have a total of 5 succeeded builds, no new builds will be created
+ # When the last one succeeds we have 4 successful builds.
+ #
succeed_pending
- expect(process_pipeline).to be_falsey
- expect(builds.success.count).to eq(5)
- expect(all_builds.count).to eq(5)
+ process_pipeline
+
+ expect(builds.success.count).to eq(4)
+ expect(all_builds.count).to eq(4)
end
end
end
+
+ def all_builds
+ pipeline.builds
+ end
+
+ def builds
+ all_builds.where.not(status: [:created, :skipped])
+ end
+
+ def process_pipeline
+ described_class.new(pipeline.project, user).execute(pipeline)
+ end
+
+ def succeed_pending
+ builds.pending.update_all(status: 'success')
+ end
+
+ def manual_actions
+ pipeline.manual_actions
+ end
+
+ def create_build(name, stage_idx, when_value = nil)
+ create(:ci_build,
+ :created,
+ pipeline: pipeline,
+ name: name,
+ stage_idx: stage_idx,
+ when: when_value)
+ end
end
diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb
new file mode 100644
index 00000000000..12c3cdf28c6
--- /dev/null
+++ b/spec/services/discussions/resolve_service_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Discussions::ResolveService do
+ describe '#execute' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:project) { merge_request.project }
+ let(:merge_request) { discussion.noteable }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ it "doesn't resolve discussions the user can't resolve" do
+ expect(discussion).to receive(:can_resolve?).with(user).and_return(false)
+
+ service.execute(discussion)
+
+ expect(discussion.resolved?).to be(false)
+ end
+
+ it 'resolves the discussion' do
+ service.execute(discussion)
+
+ expect(discussion.resolved?).to be(true)
+ end
+
+ it 'executes the notification service' do
+ expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(discussion.noteable)
+
+ service.execute(discussion)
+ end
+
+ it 'adds a system note to the discussion' do
+ issue = create(:issue, project: project)
+
+ expect(SystemNoteService).to receive(:discussion_continued_in_issue).with(discussion, project, user, issue)
+ service = described_class.new(project, user, merge_request: merge_request, follow_up_issue: issue)
+ service.execute(discussion)
+ end
+
+ it 'can resolve multiple discussions at once' do
+ other_discussion = Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: discussion.noteable, project: discussion.noteable.source_project)]).first
+
+ service.execute([discussion, other_discussion])
+
+ expect(discussion.resolved?).to be(true)
+ expect(other_discussion.resolved?).to be(true)
+ end
+ end
+end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 9d7702f5c96..e7624e70725 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -263,7 +263,7 @@ describe GitPushService, services: true do
author_email: commit_author.email
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -321,7 +321,7 @@ describe GitPushService, services: true do
committed_date: commit_time
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -360,7 +360,7 @@ describe GitPushService, services: true do
allow(project.repository).to receive(:commits_between).
and_return([closing_commit])
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(closing_commit)
project.team << [commit_author, :master]
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
new file mode 100644
index 00000000000..4cfba35c830
--- /dev/null
+++ b/spec/services/issues/build_service_spec.rb
@@ -0,0 +1,130 @@
+require 'spec_helper.rb'
+
+describe Issues::BuildService, services: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'for discussions in a merge request' do
+ let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
+ let(:issue) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute }
+
+ def position_on_line(line_number)
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: line_number,
+ diff_refs: merge_request.diff_refs
+ )
+ end
+
+ describe '#items_for_discussions' do
+ it 'has an item for each discussion' do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.source_project, position: position_on_line(13))
+ service = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request)
+
+ service.execute
+
+ expect(service.items_for_discussions.size).to eq(2)
+ end
+ end
+
+ describe '#item_for_discussion' do
+ let(:service) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request) }
+
+ it 'mentions the author of the note' do
+ discussion = Discussion.new([create(:diff_note_on_merge_request, author: create(:user, username: 'author'))])
+ expect(service.item_for_discussion(discussion)).to include('@author')
+ end
+
+ it 'wraps the note in a blockquote' do
+ note_text = "This is a string\n"\
+ ">>>\n"\
+ "with a blockquote\n"\
+ "> That has a quote\n"\
+ ">>>\n"
+ note_result = "This is a string\n"\
+ "> with a blockquote\n"\
+ "> > That has a quote\n"
+ discussion = Discussion.new([create(:diff_note_on_merge_request, note: note_text)])
+ expect(service.item_for_discussion(discussion)).to include(">>>\n#{note_result}\n>>>")
+ end
+ end
+
+ describe '#execute' do
+ it 'has the merge request reference in the title' do
+ expect(issue.title).to include(merge_request.title)
+ end
+
+ it 'has the reference of the merge request in the description' do
+ expect(issue.description).to include(merge_request.to_reference)
+ end
+
+ it 'does not assign title when a title was given' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ title: 'What an issue').execute
+
+ expect(issue.title).to eq('What an issue')
+ end
+
+ it 'does not assign description when a description was given' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ description: 'Fix at your earliest conveignance').execute
+
+ expect(issue.description).to eq('Fix at your earliest conveignance')
+ end
+
+ describe 'with multiple discussions' do
+ before do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15))
+ end
+
+ it 'mentions all the authors in the description' do
+ authors = merge_request.diff_discussions.map(&:author)
+
+ expect(issue.description).to include(*authors.map(&:to_reference))
+ end
+
+ it 'has a link for each unresolved discussion in the description' do
+ notes = merge_request.diff_discussions.map(&:first_note)
+ links = notes.map { |note| Gitlab::UrlBuilder.build(note) }
+
+ expect(issue.description).to include(*links)
+ end
+
+ it 'mentions additional notes' do
+ create_list(:diff_note_on_merge_request, 2, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15))
+
+ expect(issue.description).to include('(+2 comments)')
+ end
+ end
+ end
+ end
+
+ context 'For a merge request without discussions' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ describe '#execute' do
+ it 'mentions the merge request in the description' do
+ issue = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute
+
+ expect(issue.description).to include("Review the conversation in #{merge_request.to_reference}")
+ end
+ end
+ end
+
+ describe '#execute' do
+ it 'builds a new issues with given params' do
+ issue = described_class.new(project, user, title: 'Issue #1', description: 'Issue description').execute
+
+ expect(issue.title).to eq('Issue #1')
+ expect(issue.description).to eq('Issue description')
+ end
+ end
+end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 5c0331ebe66..8bde61ee336 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -136,5 +136,48 @@ describe Issues::CreateService, services: true do
end
it_behaves_like 'new issuable record that supports slash commands'
+
+ context 'for a merge request' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ let(:opts) { { merge_request_for_resolving_discussions: merge_request } }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'resolves the discussion for the merge request' do
+ described_class.new(project, user, opts).execute
+ discussion.first_note.reload
+
+ expect(discussion.resolved?).to be(true)
+ end
+
+ it 'added a system note to the discussion' do
+ described_class.new(project, user, opts).execute
+
+ reloaded_discussion = MergeRequest.find(merge_request.id).discussions.first
+
+ expect(reloaded_discussion.last_note.system).to eq(true)
+ end
+
+ it 'assigns the title and description for the issue' do
+ issue = described_class.new(project, user, opts).execute
+
+ expect(issue.title).not_to be_nil
+ expect(issue.description).not_to be_nil
+ end
+
+ it 'can set nil explicityly to the title and description' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ description: nil,
+ title: nil).execute
+
+ expect(issue.description).to be_nil
+ expect(issue.title).to be_nil
+ end
+ end
end
end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index c7de0d0c534..db196ed5751 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -189,7 +189,7 @@ describe Issues::MoveService, services: true do
it 'rewrites references using a cross reference to old project' do
expect(new_note.note)
- .to eq "Note with reference to merge request #{old_project.to_reference}!1"
+ .to eq "Note with reference to merge request #{old_project.to_reference(new_project)}!1"
end
end
@@ -217,7 +217,7 @@ describe Issues::MoveService, services: true do
it 'rewrites referenced issues creating cross project reference' do
expect(new_issue.description)
- .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
+ .to eq "Some description #{another_issue.to_reference(new_project)}"
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 4c878d748c0..500d224ff98 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -2,6 +2,8 @@
require 'spec_helper'
describe Issues::UpdateService, services: true do
+ include EmailHelpers
+
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
index a44312dd363..bb7830c7eea 100644
--- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -20,13 +20,19 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
let(:todo_service) { TodoService.new }
let(:merge_request) do
- create(:merge_request, merge_user: user, source_branch: 'master',
- target_branch: 'feature', source_project: project, target_project: project,
+ create(:merge_request, merge_user: user,
+ source_branch: 'master',
+ target_branch: 'feature',
+ source_project: project,
+ target_project: project,
state: 'opened')
end
before do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
allow(service).to receive(:todo_service).and_return(todo_service)
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index dff1781d2aa..5a89acc96a4 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -75,7 +75,7 @@ describe MergeRequests::MergeService, services: true do
commit = double('commit', safe_message: "Fixes #{jira_issue.to_reference}")
allow(merge_request).to receive(:commits).and_return([commit])
- expect_any_instance_of(JiraService).to receive(:close_issue).with(merge_request, an_instance_of(JIRA::Resource::Issue)).once
+ expect_any_instance_of(JiraService).to receive(:close_issue).with(merge_request, jira_issue).once
service.execute(merge_request)
end
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index c0164138713..f92978a33a3 100644
--- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::MergeWhenBuildSucceedsService do
+describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -10,8 +10,14 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
source_project: project, target_project: project, state: "opened")
end
- let(:pipeline) { create(:ci_pipeline_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) }
- let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') }
+ let(:pipeline) do
+ create(:ci_pipeline_with_one_job, ref: mr_merge_if_green_enabled.source_branch,
+ project: project)
+ end
+
+ let(:service) do
+ described_class.new(project, user, commit_message: 'Awesome message')
+ end
describe "#execute" do
let(:merge_request) do
@@ -21,7 +27,10 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
context 'first time enabling' do
before do
- allow(merge_request).to receive(:pipeline).and_return(pipeline)
+ allow(merge_request)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
service.execute(merge_request)
end
@@ -34,17 +43,21 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
it 'creates a system note' do
note = merge_request.notes.last
- expect(note.note).to match /enabled an automatic merge when the build for (\w+\/\w+@)?\h{8}/
+ expect(note.note).to match /enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{8}/
end
end
context 'already approved' do
- let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, new_key: true) }
+ let(:service) { described_class.new(project, user, new_key: true) }
let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) }
before do
- allow(mr_merge_if_green_enabled).to receive(:pipeline).and_return(pipeline)
- allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true)
+ allow(mr_merge_if_green_enabled).to receive(:head_pipeline)
+ .and_return(pipeline)
+
+ allow(mr_merge_if_green_enabled).to receive(:mergeable?)
+ .and_return(true)
+
allow(pipeline).to receive(:success?).and_return(true)
end
@@ -138,9 +151,12 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
before do
# This behavior of MergeRequest: we instantiate a new object
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_wrap_original do
- Ci::Pipeline.find(pipeline.id)
- end
+ #
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_wrap_original do
+ Ci::Pipeline.find(pipeline.id)
+ end
end
it "doesn't merge if any of stages failed" do
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 0bd6db1810a..790ef765f3a 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe MergeRequests::UpdateService, services: true do
+ include EmailHelpers
+
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 08ae61708a5..f3e80ac22a0 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe NotificationService, services: true do
+ include EmailHelpers
+
let(:notification) { NotificationService.new }
around(:each) do |example|
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index fbd22560d6e..a1539b69401 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -1,150 +1,150 @@
require 'spec_helper'
-describe Projects::CreateService, services: true do
- describe :create_by_user do
- before do
- @user = create :user
- @opts = {
- name: "GitLab",
- namespace: @user.namespace
- }
- end
+describe Projects::CreateService, '#execute', services: true do
+ let(:user) { create :user }
+ let(:opts) do
+ {
+ name: "GitLab",
+ namespace: user.namespace
+ }
+ end
- it 'creates labels on Project creation if there are templates' do
- Label.create(title: "bug", template: true)
- project = create_project(@user, @opts)
- project.reload
+ it 'creates labels on Project creation if there are templates' do
+ Label.create(title: "bug", template: true)
+ project = create_project(user, opts)
- expect(project.labels).not_to be_empty
- end
+ expect(project.labels).not_to be_empty
+ end
- context 'user namespace' do
- before do
- @project = create_project(@user, @opts)
- end
+ context 'user namespace' do
+ it do
+ project = create_project(user, opts)
- it { expect(@project).to be_valid }
- it { expect(@project.owner).to eq(@user) }
- it { expect(@project.team.masters).to include(@user) }
- it { expect(@project.namespace).to eq(@user.namespace) }
+ expect(project).to be_valid
+ expect(project.owner).to eq(user)
+ expect(project.team.masters).to include(user)
+ expect(project.namespace).to eq(user.namespace)
end
+ end
- context 'group namespace' do
- before do
- @group = create :group
- @group.add_owner(@user)
+ context 'group namespace' do
+ let(:group) do
+ create(:group).tap do |group|
+ group.add_owner(user)
+ end
+ end
- @user.refresh_authorized_projects # Ensure cache is warm
+ before do
+ user.refresh_authorized_projects # Ensure cache is warm
+ end
- @opts.merge!(namespace_id: @group.id)
- @project = create_project(@user, @opts)
- end
+ it do
+ project = create_project(user, opts.merge!(namespace_id: group.id))
- it { expect(@project).to be_valid }
- it { expect(@project.owner).to eq(@group) }
- it { expect(@project.namespace).to eq(@group) }
- it { expect(@user.authorized_projects).to include(@project) }
+ expect(project).to be_valid
+ expect(project.owner).to eq(group)
+ expect(project.namespace).to eq(group)
+ expect(user.authorized_projects).to include(project)
end
+ end
- context 'error handling' do
- it 'handles invalid options' do
- @opts.merge!({ default_branch: 'master' } )
- expect(create_project(@user, @opts)).to eq(nil)
- end
+ context 'error handling' do
+ it 'handles invalid options' do
+ opts.merge!({ default_branch: 'master' } )
+ expect(create_project(user, opts)).to eq(nil)
end
+ end
- context 'wiki_enabled creates repository directory' do
- context 'wiki_enabled true creates wiki repository directory' do
- before do
- @project = create_project(@user, @opts)
- @path = ProjectWiki.new(@project, @user).send(:path_to_repo)
- end
+ context 'wiki_enabled creates repository directory' do
+ context 'wiki_enabled true creates wiki repository directory' do
+ it do
+ project = create_project(user, opts)
+ path = ProjectWiki.new(project, user).send(:path_to_repo)
- it { expect(File.exist?(@path)).to be_truthy }
+ expect(File.exist?(path)).to be_truthy
end
+ end
- context 'wiki_enabled false does not create wiki repository directory' do
- before do
- @opts.merge!(wiki_enabled: false)
- @project = create_project(@user, @opts)
- @path = ProjectWiki.new(@project, @user).send(:path_to_repo)
- end
+ context 'wiki_enabled false does not create wiki repository directory' do
+ it do
+ opts.merge!(wiki_enabled: false)
+ project = create_project(user, opts)
+ path = ProjectWiki.new(project, user).send(:path_to_repo)
- it { expect(File.exist?(@path)).to be_falsey }
+ expect(File.exist?(path)).to be_falsey
end
end
+ end
- context 'builds_enabled global setting' do
- let(:project) { create_project(@user, @opts) }
-
- subject { project.builds_enabled? }
+ context 'builds_enabled global setting' do
+ let(:project) { create_project(user, opts) }
- context 'global builds_enabled false does not enable CI by default' do
- before do
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
- end
+ subject { project.builds_enabled? }
- it { is_expected.to be_falsey }
+ context 'global builds_enabled false does not enable CI by default' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
end
- context 'global builds_enabled true does enable CI by default' do
- before do
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
- end
+ it { is_expected.to be_falsey }
+ end
- it { is_expected.to be_truthy }
+ context 'global builds_enabled true does enable CI by default' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
end
+
+ it { is_expected.to be_truthy }
end
+ end
- context 'restricted visibility level' do
- before do
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ context 'restricted visibility level' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
- @opts.merge!(
- visibility_level: Gitlab::VisibilityLevel.options['Public']
- )
- end
+ opts.merge!(
+ visibility_level: Gitlab::VisibilityLevel.options['Public']
+ )
+ end
- it 'does not allow a restricted visibility level for non-admins' do
- project = create_project(@user, @opts)
- expect(project).to respond_to(:errors)
- expect(project.errors.messages).to have_key(:visibility_level)
- expect(project.errors.messages[:visibility_level].first).to(
- match('restricted by your GitLab administrator')
- )
- end
+ it 'does not allow a restricted visibility level for non-admins' do
+ project = create_project(user, opts)
+ expect(project).to respond_to(:errors)
+ expect(project.errors.messages).to have_key(:visibility_level)
+ expect(project.errors.messages[:visibility_level].first).to(
+ match('restricted by your GitLab administrator')
+ )
+ end
- it 'allows a restricted visibility level for admins' do
- admin = create(:admin)
- project = create_project(admin, @opts)
+ it 'allows a restricted visibility level for admins' do
+ admin = create(:admin)
+ project = create_project(admin, opts)
- expect(project.errors.any?).to be(false)
- expect(project.saved?).to be(true)
- end
+ expect(project.errors.any?).to be(false)
+ expect(project.saved?).to be(true)
end
+ end
- context 'repository creation' do
- it 'synchronously creates the repository' do
- expect_any_instance_of(Project).to receive(:create_repository)
+ context 'repository creation' do
+ it 'synchronously creates the repository' do
+ expect_any_instance_of(Project).to receive(:create_repository)
- project = create_project(@user, @opts)
- expect(project).to be_valid
- expect(project.owner).to eq(@user)
- expect(project.namespace).to eq(@user.namespace)
- end
+ project = create_project(user, opts)
+ expect(project).to be_valid
+ expect(project.owner).to eq(user)
+ expect(project.namespace).to eq(user.namespace)
end
+ end
- context 'when there is an active service template' do
- before do
- create(:service, project: nil, template: true, active: true)
- end
+ context 'when there is an active service template' do
+ before do
+ create(:service, project: nil, template: true, active: true)
+ end
- it 'creates a service from this template' do
- project = create_project(@user, @opts)
- project.reload
+ it 'creates a service from this template' do
+ project = create_project(user, opts)
- expect(project.services.count).to eq 1
- end
+ expect(project.services.count).to eq 1
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 7dcd03496bb..90771825f5c 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -7,15 +7,21 @@ describe Projects::DestroyService, services: true do
let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
let!(:async) { false } # execute or async_execute
+ shared_examples 'deleting the project' do
+ it 'deletes the project' do
+ expect(Project.all).not_to include(project)
+ expect(Dir.exist?(path)).to be_falsey
+ expect(Dir.exist?(remove_path)).to be_falsey
+ end
+ end
+
context 'Sidekiq inline' do
before do
# Run sidekiq immediatly to check that renamed repository will be removed
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
- it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exist?(path)).to be_falsey }
- it { expect(Dir.exist?(remove_path)).to be_falsey }
+ it_behaves_like 'deleting the project'
end
context 'Sidekiq fake' do
@@ -38,11 +44,21 @@ describe Projects::DestroyService, services: true do
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
- it 'deletes the project' do
- expect(Project.all).not_to include(project)
- expect(Dir.exist?(path)).to be_falsey
- expect(Dir.exist?(remove_path)).to be_falsey
+ it_behaves_like 'deleting the project'
+ end
+
+ context 'delete with pipeline' do # which has optimistic locking
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+ before do
+ expect(project).to receive(:destroy!).and_call_original
+
+ perform_enqueued_jobs do
+ destroy_project(project, user, {})
+ end
end
+
+ it_behaves_like 'deleting the project'
end
context 'container registry' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 4a8f6c321aa..90b7e62bc6f 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -225,8 +225,8 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
- it "posts the Merge When Build Succeeds system note" do
- expect(subject.note).to match /enabled an automatic merge when the build for (\w+\/\w+@)?\h{40} succeeds/
+ it "posts the 'merge when pipeline succeeds' system note" do
+ expect(subject.note).to match /enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{40} succeeds/
end
end
@@ -239,7 +239,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
- it "posts the Merge When Build Succeeds system note" do
+ it "posts the 'merge when pipeline succeeds' system note" do
expect(subject.note).to eq "canceled the automatic merge"
end
end
@@ -530,7 +530,7 @@ describe SystemNoteService, services: true do
end
it 'mentions referenced project' do
- expect(subject.note).to include new_project.to_reference
+ expect(subject.note).to include new_project.path_with_namespace
end
end
@@ -712,4 +712,32 @@ describe SystemNoteService, services: true do
end
end
end
+
+ describe '.discussion_continued_in_issue' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ let(:issue) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+
+ def reloaded_merge_request
+ MergeRequest.find(merge_request.id)
+ end
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'creates a new note in the discussion' do
+ # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded.
+ expect { SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) }.
+ to change { reloaded_merge_request.discussions.first.notes.size }.by(1)
+ end
+
+ it 'mentions the created issue in the system note' do
+ note = SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue)
+
+ expect(note.note).to include(issue.to_reference)
+ end
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bead1a006d1..6ee3307512d 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -32,7 +32,7 @@ RSpec.configure do |config|
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
config.include StubConfiguration
- config.include EmailHelpers
+ config.include EmailHelpers, type: :mailer
config.include TestEnv
config.include ActiveJob::TestHelper
config.include ActiveSupport::Testing::TimeHelpers
@@ -55,8 +55,12 @@ RSpec.configure do |config|
config.around(:each, :redis) do |example|
Gitlab::Redis.with(&:flushall)
+ Sidekiq.redis(&:flushall)
+
example.run
+
Gitlab::Redis.with(&:flushall)
+ Sidekiq.redis(&:flushall)
end
end
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index 691d7e05f57..ceddb656596 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -7,7 +7,7 @@ module AccessMatchers
extend RSpec::Matchers::DSL
include Warden::Test::Helpers
- def emulate_user(user, project = nil)
+ def emulate_user(user, membership = nil)
case user
when :user
login_as(create(:user))
@@ -19,16 +19,17 @@ module AccessMatchers
login_as(create(:user, external: true))
when User
login_as(user)
- when :owner
- raise ArgumentError, "cannot emulate owner without project" unless project
-
- login_as(project.owner)
- when *Gitlab::Access.sym_options.keys
- raise ArgumentError, "cannot emulate user #{user} without project" unless project
+ when *Gitlab::Access.sym_options_with_owner.keys
+ raise ArgumentError, "cannot emulate #{user} without membership parent" unless membership
role = user
- user = create(:user)
- project.public_send(:"add_#{role}", user)
+
+ if role == :owner && membership.owner
+ user = membership.owner
+ else
+ user = create(:user)
+ membership.public_send(:"add_#{role}", user)
+ end
login_as(user)
else
@@ -47,14 +48,14 @@ module AccessMatchers
matcher :be_allowed_for do |user|
match do |url|
- emulate_user(user, @project)
+ emulate_user(user, @membership)
visit(url)
status_code != 404 && current_path != new_user_session_path
end
- chain :of do |project|
- @project = project
+ chain :of do |membership|
+ @membership = membership
end
description { description_for(user, 'allowed') }
@@ -62,14 +63,14 @@ module AccessMatchers
matcher :be_denied_for do |user|
match do |url|
- emulate_user(user, @project)
+ emulate_user(user, @membership)
visit(url)
status_code == 404 || current_path == new_user_session_path
end
- chain :of do |project|
- @project = project
+ chain :of do |membership|
+ @membership = membership
end
description { description_for(user, 'denied') }
diff --git a/spec/support/matchers/have_issuable_counts.rb b/spec/support/matchers/have_issuable_counts.rb
index 02605d6b70e..92cf3de5448 100644
--- a/spec/support/matchers/have_issuable_counts.rb
+++ b/spec/support/matchers/have_issuable_counts.rb
@@ -1,9 +1,9 @@
RSpec::Matchers.define :have_issuable_counts do |opts|
- match do |actual|
- expected_counts = opts.map do |state, count|
- "#{state.to_s.humanize} #{count}"
- end
+ expected_counts = opts.map do |state, count|
+ "#{state.to_s.humanize} #{count}"
+ end
+ match do |actual|
actual.within '.issues-state-filters' do
expected_counts.each do |expected_count|
expect(actual).to have_content(expected_count)
diff --git a/spec/support/matchers/is_within.rb b/spec/support/matchers/is_within.rb
deleted file mode 100644
index 0c35fc7e899..00000000000
--- a/spec/support/matchers/is_within.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# Extend shoulda-matchers
-module Shoulda::Matchers::ActiveModel
- class ValidateLengthOfMatcher
- # Shortcut for is_at_least and is_at_most
- def is_within(range)
- is_at_least(range.min) && is_at_most(range.max)
- end
- end
-end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 8c98b1f988c..97b8b342eb2 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -38,9 +38,9 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('h1 a#gitlab-markdown')
- expect(actual).to have_selector('h2 a#markdown')
- expect(actual).to have_selector('h3 a#autolinkfilter')
+ expect(actual).to have_selector('h1 a#user-content-gitlab-markdown')
+ expect(actual).to have_selector('h2 a#user-content-markdown')
+ expect(actual).to have_selector('h3 a#user-content-autolinkfilter')
end
end
diff --git a/spec/support/rake_helpers.rb b/spec/support/rake_helpers.rb
index 52d80c69835..4a8158ed79b 100644
--- a/spec/support/rake_helpers.rb
+++ b/spec/support/rake_helpers.rb
@@ -1,7 +1,7 @@
module RakeHelpers
- def run_rake_task(task_name)
+ def run_rake_task(task_name, *args)
Rake::Task[task_name].reenable
- Rake.application.invoke_task task_name
+ Rake.application.invoke_task("#{task_name}[#{args.join(',')}]")
end
def stub_warn_user_is_not_gitlab
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 287d83344db..a9fea5f1e81 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -5,7 +5,7 @@ describe 'gitlab:app namespace rake task' do
let(:enable_registry) { true }
before :all do
- Rake.application.rake_require 'tasks/gitlab/task_helpers'
+ Rake.application.rake_require 'tasks/gitlab/helpers'
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/gitlab/db'
@@ -333,4 +333,35 @@ describe 'gitlab:app namespace rake task' do
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
end
+
+ describe "Human Readable Backup Name" do
+ def tars_glob
+ Dir.glob(File.join(Gitlab.config.backup.path, '*_gitlab_backup.tar'))
+ end
+
+ before :all do
+ @origin_cd = Dir.pwd
+
+ reenable_backup_sub_tasks
+
+ FileUtils.rm tars_glob
+
+ # Redirect STDOUT and run the rake task
+ orig_stdout = $stdout
+ $stdout = StringIO.new
+ run_rake_task('gitlab:backup:create')
+ $stdout = orig_stdout
+
+ @backup_tar = tars_glob.first
+ end
+
+ after :all do
+ FileUtils.rm(@backup_tar)
+ Dir.chdir @origin_cd
+ end
+
+ it 'name has human readable time' do
+ expect(@backup_tar).to match(/\d+_\d{4}_\d{2}_\d{2}_gitlab_backup.tar$/)
+ end
+ end
end # gitlab:app namespace
diff --git a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
index 37feb5e6faf..80fc8c48fed 100644
--- a/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
+++ b/spec/tasks/gitlab/mail_google_schema_whitelisting.rb
@@ -3,7 +3,7 @@ require 'rake'
describe 'gitlab:mail_google_schema_whitelisting rake task' do
before :all do
- Rake.application.rake_require "tasks/gitlab/task_helpers"
+ Rake.application.rake_require "tasks/gitlab/helpers"
Rake.application.rake_require "tasks/gitlab/mail_google_schema_whitelisting"
# empty task as env is already loaded
Rake::Task.define_task :environment
diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb
new file mode 100644
index 00000000000..86e42d845ce
--- /dev/null
+++ b/spec/tasks/gitlab/task_helpers_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+require 'tasks/gitlab/task_helpers'
+
+class TestHelpersTest
+ include Gitlab::TaskHelpers
+end
+
+describe Gitlab::TaskHelpers do
+ subject { TestHelpersTest.new }
+
+ let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-test.git' }
+ let(:clone_path) { Rails.root.join('tmp/tests/task_helpers_tests').to_s }
+ let(:tag) { 'v1.1.0' }
+
+ describe '#checkout_or_clone_tag' do
+ before do
+ allow(subject).to receive(:run_command!)
+ expect(subject).to receive(:reset_to_tag).with(tag, clone_path)
+ end
+
+ context 'target_dir does not exist' do
+ it 'clones the repo, retrieve the tag from origin, and checkout the tag' do
+ expect(subject).to receive(:clone_repo).with(repo, clone_path)
+
+ subject.checkout_or_clone_tag(tag: tag, repo: repo, target_dir: clone_path)
+ end
+ end
+
+ context 'target_dir exists' do
+ before do
+ expect(Dir).to receive(:exist?).and_return(true)
+ end
+
+ it 'fetch and checkout the tag' do
+ expect(subject).to receive(:checkout_tag).with(tag, clone_path)
+
+ subject.checkout_or_clone_tag(tag: tag, repo: repo, target_dir: clone_path)
+ end
+ end
+ end
+
+ describe '#clone_repo' do
+ it 'clones the repo in the target dir' do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{clone_path}])
+
+ subject.clone_repo(repo, clone_path)
+ end
+ end
+
+ describe '#checkout_tag' do
+ it 'clones the repo in the target dir' do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --tags --quiet])
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
+
+ subject.checkout_tag(tag, clone_path)
+ end
+ end
+
+ describe '#reset_to_tag' do
+ let(:tag) { 'v1.1.0' }
+ before do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
+ end
+
+ context 'when the tag is not checked out locally' do
+ before do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- #{tag}]).and_raise(Gitlab::TaskFailedError)
+ end
+
+ it 'fetch origin, ensure the tag exists, and resets --hard to the given tag' do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch origin])
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- origin/#{tag}]).and_return(tag)
+
+ subject.reset_to_tag(tag, clone_path)
+ end
+ end
+
+ context 'when the tag is checked out locally' do
+ before do
+ expect(subject).
+ to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} describe -- #{tag}]).and_return(tag)
+ end
+
+ it 'resets --hard to the given tag' do
+ subject.reset_to_tag(tag, clone_path)
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/users_rake_spec.rb b/spec/tasks/gitlab/users_rake_spec.rb
index e6ebef82b78..972670e7f91 100644
--- a/spec/tasks/gitlab/users_rake_spec.rb
+++ b/spec/tasks/gitlab/users_rake_spec.rb
@@ -5,7 +5,7 @@ describe 'gitlab:users namespace rake task' do
let(:enable_registry) { true }
before :all do
- Rake.application.rake_require 'tasks/gitlab/task_helpers'
+ Rake.application.rake_require 'tasks/gitlab/helpers'
Rake.application.rake_require 'tasks/gitlab/users'
# empty task as env is already loaded
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
new file mode 100644
index 00000000000..6de66c3cf07
--- /dev/null
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -0,0 +1,81 @@
+require 'rake_helper'
+
+describe 'gitlab:workhorse namespace rake task' do
+ before :all do
+ Rake.application.rake_require 'tasks/gitlab/workhorse'
+ end
+
+ describe 'install' do
+ let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
+ let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
+ let(:tag) { "v#{File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp}" }
+ before do
+ allow(ENV).to receive(:[])
+ end
+
+ context 'no dir given' do
+ it 'aborts and display a help message' do
+ # avoid writing task output to spec progress
+ allow($stderr).to receive :write
+ expect { run_rake_task('gitlab:workhorse:install') }.to raise_error /Please specify the directory where you want to install gitlab-workhorse/
+ end
+ end
+
+ context 'when an underlying Git command fail' do
+ it 'aborts and display a help message' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).and_raise 'Git error'
+
+ expect { run_rake_task('gitlab:workhorse:install', clone_path) }.to raise_error 'Git error'
+ end
+ end
+
+ describe 'checkout or clone' do
+ before do
+ expect(Dir).to receive(:chdir).with(clone_path)
+ end
+
+ it 'calls checkout_or_clone_tag with the right arguments' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path)
+
+ run_rake_task('gitlab:workhorse:install', clone_path)
+ end
+ end
+
+ describe 'gmake/make' do
+ before do
+ FileUtils.mkdir_p(clone_path)
+ expect(Dir).to receive(:chdir).with(clone_path).and_call_original
+ end
+
+ context 'gmake is available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ end
+
+ it 'calls gmake in the gitlab-workhorse directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+
+ run_rake_task('gitlab:workhorse:install', clone_path)
+ end
+ end
+
+ context 'gmake is not available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+ end
+
+ it 'calls make in the gitlab-workhorse directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+
+ run_rake_task('gitlab:workhorse:install', clone_path)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/workers/build_email_worker_spec.rb b/spec/workers/build_email_worker_spec.rb
index a1aa336361a..542e674c150 100644
--- a/spec/workers/build_email_worker_spec.rb
+++ b/spec/workers/build_email_worker_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
describe BuildEmailWorker do
+ include EmailHelpers
include RepoHelpers
let(:build) { create(:ci_build) }
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index fc652f6f4c3..f27e413f7b8 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe EmailsOnPushWorker do
include RepoHelpers
+ include EmailHelpers
include EmailSpec::Matchers
let(:project) { create(:project) }
diff --git a/spec/workers/pipeline_notification_worker_spec.rb b/spec/workers/pipeline_notification_worker_spec.rb
index d487a719680..603ae52ed1e 100644
--- a/spec/workers/pipeline_notification_worker_spec.rb
+++ b/spec/workers/pipeline_notification_worker_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe PipelineNotificationWorker do
+ include EmailHelpers
+
let(:pipeline) do
create(:ci_pipeline,
project: project,
@@ -9,7 +11,7 @@ describe PipelineNotificationWorker do
status: status)
end
- let(:project) { create(:project) }
+ let(:project) { create(:project, public_builds: false) }
let(:user) { create(:user) }
let(:pusher) { user }
let(:watcher) { pusher }
diff --git a/spec/workers/pipeline_success_worker_spec.rb b/spec/workers/pipeline_success_worker_spec.rb
index 5e31cc2c8e7..d1c84adda6f 100644
--- a/spec/workers/pipeline_success_worker_spec.rb
+++ b/spec/workers/pipeline_success_worker_spec.rb
@@ -7,7 +7,7 @@ describe PipelineSuccessWorker do
it 'performs "merge when pipeline succeeds"' do
expect_any_instance_of(
- MergeRequests::MergeWhenBuildSucceedsService
+ MergeRequests::MergeWhenPipelineSucceedsService
).to receive(:trigger)
described_class.new.perform(pipeline.id)
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 3e4fee42240..75c7fc1efd2 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -11,31 +11,25 @@ describe ProcessCommitWorker do
it 'does not process the commit when the project does not exist' do
expect(worker).not_to receive(:close_issues)
- worker.perform(-1, user.id, commit.id)
+ worker.perform(-1, user.id, commit.to_hash)
end
it 'does not process the commit when the user does not exist' do
expect(worker).not_to receive(:close_issues)
- worker.perform(project.id, -1, commit.id)
- end
-
- it 'does not process the commit when the commit no longer exists' do
- expect(worker).not_to receive(:close_issues)
-
- worker.perform(project.id, user.id, 'this-should-does-not-exist')
+ worker.perform(project.id, -1, commit.to_hash)
end
it 'processes the commit message' do
expect(worker).to receive(:process_commit_message).and_call_original
- worker.perform(project.id, user.id, commit.id)
+ worker.perform(project.id, user.id, commit.to_hash)
end
it 'updates the issue metrics' do
expect(worker).to receive(:update_issue_metrics).and_call_original
- worker.perform(project.id, user.id, commit.id)
+ worker.perform(project.id, user.id, commit.to_hash)
end
end
@@ -106,4 +100,19 @@ describe ProcessCommitWorker do
expect(metric.first_mentioned_in_commit_at).to eq(commit.committed_date)
end
end
+
+ describe '#build_commit' do
+ it 'returns a Commit' do
+ commit = worker.build_commit(project, id: '123')
+
+ expect(commit).to be_an_instance_of(Commit)
+ end
+
+ it 'parses date strings into Time instances' do
+ commit = worker.
+ build_commit(project, id: '123', authored_date: Time.now.to_s)
+
+ expect(commit.authored_date).to be_an_instance_of(Time)
+ end
+ end
end