summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-05-03 10:17:05 +0100
committerPhil Hughes <me@iamphill.com>2018-05-03 10:17:05 +0100
commit9b8332d324c931798c30adf5517abda96212d5c3 (patch)
tree939dcc2dfcb621aca3669820bb6913988a51cb30 /spec
parent363ebd3efc5cc0a5130664aa0676bff14a011a54 (diff)
parentd1cdd879d006f0dfd9db8b02f595b3299b791d02 (diff)
downloadgitlab-ce-9b8332d324c931798c30adf5517abda96212d5c3.tar.gz
Merge branch 'master' into 44846-improve-web-ide-left-panel-and-modes
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb30
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb4
-rw-r--r--spec/factories/ci/stages.rb1
-rw-r--r--spec/factories/commit_statuses.rb1
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/dashboard/groups_list_spec.rb22
-rw-r--r--spec/features/groups/members/manage_access_requests_spec.rb47
-rw-r--r--spec/features/groups/members/master_manages_access_requests_spec.rb8
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb32
-rw-r--r--spec/features/profiles/active_sessions_spec.rb89
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb240
-rw-r--r--spec/features/projects/issues/user_toggles_subscription_spec.rb8
-rw-r--r--spec/features/projects/members/master_manages_access_requests_spec.rb45
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb18
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb280
-rw-r--r--spec/features/users/active_sessions_spec.rb69
-rw-r--r--spec/finders/groups_finder_spec.rb84
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/notes.json5
-rw-r--r--spec/helpers/application_helper_spec.rb139
-rw-r--r--spec/helpers/auth_helper_spec.rb24
-rw-r--r--spec/helpers/avatars_helper_spec.rb139
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js29
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/success_message_spec.js35
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js14
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js10
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js8
-rw-r--r--spec/javascripts/sidebar/mock_data.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js9
-rw-r--r--spec/javascripts/test_bundle.js12
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js8
-rw-r--r--spec/lib/banzai/filter/commit_trailers_filter_spec.rb40
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb3
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb87
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb18
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb20
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb66
-rw-r--r--spec/lib/omni_auth/strategies/jwt_spec.rb87
-rw-r--r--spec/migrations/schedule_stages_index_migration_spec.rb35
-rw-r--r--spec/models/active_session_spec.rb216
-rw-r--r--spec/models/ci/stage_spec.rb34
-rw-r--r--spec/models/diff_note_spec.rb33
-rw-r--r--spec/models/lfs_object_spec.rb4
-rw-r--r--spec/models/members/group_member_spec.rb48
-rw-r--r--spec/models/members/project_member_spec.rb12
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/models/notification_setting_spec.rb7
-rw-r--r--spec/requests/api/discussions_spec.rb33
-rw-r--r--spec/requests/api/jobs_spec.rb6
-rw-r--r--spec/requests/api/runner_spec.rb4
-rw-r--r--spec/requests/api/v3/builds_spec.rb4
-rw-r--r--spec/requests/openid_connect_spec.rb9
-rw-r--r--spec/services/applications/create_service_spec.rb14
-rw-r--r--spec/services/ci/retry_build_service_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb4
-rw-r--r--spec/services/notes/resolve_service_spec.rb23
-rw-r--r--spec/services/notification_service_spec.rb42
-rw-r--r--spec/services/projects/update_pages_service_spec.rb61
-rw-r--r--spec/services/repository_archive_clean_up_service_spec.rb68
-rw-r--r--spec/support/commit_trailers_spec_helper.rb2
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb52
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb19
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb63
-rw-r--r--spec/support/shared_examples/requests/api/diff_discussions.rb57
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions.rb87
-rw-r--r--spec/uploaders/lfs_object_uploader_spec.rb12
-rw-r--r--spec/workers/mail_scheduler/issue_due_worker_spec.rb4
-rw-r--r--spec/workers/mail_scheduler/notification_service_worker_spec.rb44
-rw-r--r--spec/workers/namespaceless_project_destroy_worker_spec.rb15
74 files changed, 1944 insertions, 857 deletions
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index e14ba29fa70..715bb9f5e52 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do
context 'when google project billing is enabled' do
before do
- redis_double = double
+ redis_double = double.as_null_object
allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 24310b847e8..00d76f3c39a 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -157,34 +157,4 @@ describe Projects::MergeRequests::CreationsController do
expect(response).to have_gitlab_http_status(200)
end
end
-
- describe 'GET #update_branches' do
- before do
- allow(Ability).to receive(:allowed?).and_call_original
- end
-
- it 'lists the branches of another fork if the user has access' do
- expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true }
-
- get :update_branches,
- namespace_id: fork_project.namespace,
- project_id: fork_project,
- target_project_id: project.id
-
- expect(assigns(:target_branches)).not_to be_empty
- expect(response).to have_gitlab_http_status(200)
- end
-
- it 'does not list branches when the user cannot read the project' do
- expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false }
-
- get :update_branches,
- namespace_id: fork_project.namespace,
- project_id: fork_project,
- target_project_id: project.id
-
- expect(response).to have_gitlab_http_status(200)
- expect(assigns(:target_branches)).to eq([])
- end
- end
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 08e2ccf893a..c3468536ae1 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -54,9 +54,9 @@ describe Projects::RawController do
end
context 'and lfs uses object storage' do
+ let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
+
before do
- lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
- lfs_object.save!
stub_lfs_object_storage
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
end
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index 25309033571..ce61e6bf759 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -21,6 +21,7 @@ FactoryBot.define do
pipeline factory: :ci_empty_pipeline
name 'test'
+ position 1
status 'pending'
end
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index ce5fbc343ee..53368c64e10 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
stage 'test'
+ stage_idx 0
status 'success'
description 'commit status'
pipeline factory: :ci_pipeline_with_one_job
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index f1ac73ff819..90cf5a53787 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -19,7 +19,7 @@ feature 'Admin uses repository checks' do
expect(page).to have_content('Repository check was triggered')
end
- scenario 'to see a single failed repository check' do
+ scenario 'to see a single failed repository check', :js do
project = create(:project)
project.update_columns(
last_repository_check_failed: true,
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index a71020002dc..ed47f7ed390 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -40,7 +40,7 @@ feature 'Dashboard Groups page', :js do
expect(page).to have_content(nested_group.name)
end
- describe 'when filtering groups', :nested_groups do
+ context 'when filtering groups', :nested_groups do
before do
group.add_owner(user)
nested_group.add_owner(user)
@@ -75,7 +75,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'group with subgroups', :nested_groups do
+ context 'with subgroups', :nested_groups do
let!(:subgroup) { create(:group, :public, parent: group) }
before do
@@ -106,7 +106,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'when using pagination' do
+ context 'when using pagination' do
let(:group) { create(:group, created_at: 5.days.ago) }
let(:group2) { create(:group, created_at: 2.days.ago) }
@@ -141,4 +141,20 @@ feature 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group2.id}")
end
end
+
+ context 'when signed in as admin' do
+ let(:admin) { create(:admin) }
+
+ it 'shows only groups admin is member of' do
+ group.add_owner(admin)
+ expect(another_group).to be_persisted
+
+ sign_in(admin)
+ visit dashboard_groups_path
+ wait_for_requests
+
+ expect(page).to have_content(group.name)
+ expect(page).not_to have_content(another_group.name)
+ end
+ end
end
diff --git a/spec/features/groups/members/manage_access_requests_spec.rb b/spec/features/groups/members/manage_access_requests_spec.rb
deleted file mode 100644
index b83cd657ef7..00000000000
--- a/spec/features/groups/members/manage_access_requests_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'spec_helper'
-
-feature 'Groups > Members > Manage access requests' do
- let(:user) { create(:user) }
- let(:owner) { create(:user) }
- let(:group) { create(:group, :public, :access_requestable) }
-
- background do
- group.request_access(user)
- group.add_owner(owner)
- sign_in(owner)
- end
-
- scenario 'owner can see access requests' do
- visit group_group_members_path(group)
-
- expect_visible_access_request(group, user)
- end
-
- scenario 'owner can grant access' do
- visit group_group_members_path(group)
-
- expect_visible_access_request(group, user)
-
- perform_enqueued_jobs { click_on 'Grant access' }
-
- expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
- expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was granted"
- end
-
- scenario 'owner can deny access' do
- visit group_group_members_path(group)
-
- expect_visible_access_request(group, user)
-
- perform_enqueued_jobs { click_on 'Deny access' }
-
- expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
- expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{group.name} group was denied"
- end
-
- def expect_visible_access_request(group, user)
- expect(group.requesters.exists?(user_id: user)).to be_truthy
- expect(page).to have_content "Users requesting access to #{group.name} 1"
- expect(page).to have_content user.name
- end
-end
diff --git a/spec/features/groups/members/master_manages_access_requests_spec.rb b/spec/features/groups/members/master_manages_access_requests_spec.rb
new file mode 100644
index 00000000000..2fd6d1ec599
--- /dev/null
+++ b/spec/features/groups/members/master_manages_access_requests_spec.rb
@@ -0,0 +1,8 @@
+require 'spec_helper'
+
+feature 'Groups > Members > Master manages access requests' do
+ it_behaves_like 'Master manages access requests' do
+ let(:entity) { create(:group, :public, :access_requestable) }
+ let(:members_page_path) { group_group_members_path(entity) }
+ end
+end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index ff2a0e15719..ddd64fa1412 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -178,9 +178,10 @@ feature 'Issues > User uses quick actions', :js do
end
context 'when the project is valid but the user not authorized' do
- let(:project_unauthorized) {create(:project, :public)}
+ let(:project_unauthorized) { create(:project, :public) }
before do
+ gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
end
@@ -195,6 +196,7 @@ feature 'Issues > User uses quick actions', :js do
context 'when the project is invalid' do
before do
+ gitlab_sign_out
sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index dbca279569a..42c279af117 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -19,7 +19,7 @@ describe 'Merge request > User selects branches for new MR', :js do
expect(page).to have_content('Target branch')
first('.js-source-branch').click
- find('.dropdown-source-branch .dropdown-content a', match: :first).click
+ find('.js-source-branch-dropdown .dropdown-content a', match: :first).click
expect(page).to have_content "b83d6e3"
end
@@ -35,22 +35,16 @@ describe 'Merge request > User selects branches for new MR', :js do
expect(page).to have_content('Target branch')
first('.js-target-branch').click
- find('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0', match: :first).click
+ find('.js-target-branch-dropdown .dropdown-content a', text: 'v1.1.0', match: :first).click
expect(page).to have_content "b83d6e3"
end
it 'generates a diff for an orphaned branch' do
- visit project_merge_requests_path(project)
-
- page.within '.content' do
- click_link 'New merge request'
- end
- expect(page).to have_content('Source branch')
- expect(page).to have_content('Target branch')
+ visit project_new_merge_request_path(project)
find('.js-source-branch', match: :first).click
- find('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch', match: :first).click
+ find('.js-source-branch-dropdown .dropdown-content a', text: 'orphaned-branch', match: :first).click
click_button "Compare branches"
click_link "Changes"
@@ -71,19 +65,18 @@ describe 'Merge request > User selects branches for new MR', :js do
first('.js-source-branch').click
- input = find('.dropdown-source-branch .dropdown-input-field')
- input.click
- input.send_keys('orphaned-branch')
+ page.within '.js-source-branch-dropdown' do
+ input = find('.dropdown-input-field')
+ input.click
+ input.send_keys('orphaned-branch')
- find('.dropdown-source-branch .dropdown-content li', match: :first)
- source_items = all('.dropdown-source-branch .dropdown-content li')
-
- expect(source_items.count).to eq(1)
+ expect(page).to have_css('.dropdown-content li', count: 1)
+ end
first('.js-target-branch').click
- find('.dropdown-target-branch .dropdown-content li', match: :first)
- target_items = all('.dropdown-target-branch .dropdown-content li')
+ find('.js-target-branch-dropdown .dropdown-content li', match: :first)
+ target_items = all('.js-target-branch-dropdown .dropdown-content li')
expect(target_items.count).to be > 1
end
@@ -171,7 +164,6 @@ describe 'Merge request > User selects branches for new MR', :js do
page.within('.merge-request') do
click_link 'Pipelines'
- wait_for_requests
expect(page).to have_content "##{pipeline.id}"
end
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
new file mode 100644
index 00000000000..4045cfd21c4
--- /dev/null
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -0,0 +1,89 @@
+require 'rails_helper'
+
+feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
+ let(:user) do
+ create(:user).tap do |user|
+ user.current_sign_in_at = Time.current
+ end
+ end
+
+ around do |example|
+ Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
+ example.run
+ end
+ end
+
+ scenario 'User sees their active sessions' do
+ Capybara::Session.new(:session1)
+ Capybara::Session.new(:session2)
+
+ # note: headers can only be set on the non-js (aka. rack-test) driver
+ using_session :session1 do
+ Capybara.page.driver.header(
+ 'User-Agent',
+ 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0'
+ )
+
+ gitlab_sign_in(user)
+ end
+
+ # set an additional session on another device
+ using_session :session2 do
+ Capybara.page.driver.header(
+ 'User-Agent',
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]'
+ )
+
+ gitlab_sign_in(user)
+ end
+
+ using_session :session1 do
+ visit profile_active_sessions_path
+
+ expect(page).to have_content(
+ '127.0.0.1 ' \
+ 'This is your current session ' \
+ 'Firefox on Ubuntu ' \
+ 'Signed in on 12 Mar 09:06'
+ )
+
+ expect(page).to have_selector '[title="Desktop"]', count: 1
+
+ expect(page).to have_content(
+ '127.0.0.1 ' \
+ 'Last accessed on 12 Mar 09:06 ' \
+ 'Mobile Safari on iOS ' \
+ 'Signed in on 12 Mar 09:06'
+ )
+
+ expect(page).to have_selector '[title="Smartphone"]', count: 1
+ end
+ end
+
+ scenario 'User can revoke a session', :js, :redis_session_store do
+ Capybara::Session.new(:session1)
+ Capybara::Session.new(:session2)
+
+ # set an additional session in another browser
+ using_session :session2 do
+ gitlab_sign_in(user)
+ end
+
+ using_session :session1 do
+ gitlab_sign_in(user)
+ visit profile_active_sessions_path
+
+ expect(page).to have_link('Revoke', count: 1)
+
+ accept_confirm { click_on 'Revoke' }
+
+ expect(page).not_to have_link('Revoke')
+ end
+
+ using_session :session2 do
+ visit profile_active_sessions_path
+
+ expect(page).to have_content('You need to sign in or sign up before continuing.')
+ end
+ end
+end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 9c1f11f4c12..41f6c52fb8a 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -1,14 +1,12 @@
-require 'spec_helper'
+require "spec_helper"
-describe 'Projects > Files > User browses files' do
+describe "User browses files" do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
- let(:project) { create(:project, :repository, name: 'Shop') }
- let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
- let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
- let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') }
+ let(:project) { create(:project, :repository, name: "Shop") }
+ let(:project2) { create(:project, :repository, name: "Another Project", path: "another-project") }
let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) }
let(:user) { project.owner }
@@ -16,57 +14,55 @@ describe 'Projects > Files > User browses files' do
sign_in(user)
end
- it 'shows last commit for current directory' do
+ it "shows last commit for current directory" do
visit(tree_path_root_ref)
- click_link 'files'
+ click_link("files")
- last_commit = project.repository.last_commit_for_path(project.default_branch, 'files')
- page.within('.blob-commit-info') do
- expect(page).to have_content last_commit.short_id
- expect(page).to have_content last_commit.author_name
+ last_commit = project.repository.last_commit_for_path(project.default_branch, "files")
+
+ page.within(".blob-commit-info") do
+ expect(page).to have_content(last_commit.short_id).and have_content(last_commit.author_name)
end
end
- context 'when browsing the master branch' do
+ context "when browsing the master branch" do
before do
visit(tree_path_root_ref)
end
- it 'shows files from a repository' do
- expect(page).to have_content('VERSION')
- expect(page).to have_content('.gitignore')
- expect(page).to have_content('LICENSE')
+ it "shows files from a repository" do
+ expect(page).to have_content("VERSION")
+ .and have_content(".gitignore")
+ .and have_content("LICENSE")
end
- it 'shows the "Browse Directory" link' do
- click_link('files')
- click_link('History')
+ it "shows the `Browse Directory` link" do
+ click_link("files")
+ click_link("History")
- expect(page).to have_link('Browse Directory')
- expect(page).not_to have_link('Browse Code')
+ expect(page).to have_link("Browse Directory").and have_no_link("Browse Code")
end
- it 'shows the "Browse File" link' do
- page.within('.tree-table') do
- click_link('README.md')
+ it "shows the `Browse File` link" do
+ page.within(".tree-table") do
+ click_link("README.md")
end
- click_link('History')
- expect(page).to have_link('Browse File')
- expect(page).not_to have_link('Browse Files')
+ click_link("History")
+
+ expect(page).to have_link("Browse File").and have_no_link("Browse Files")
end
- it 'shows the "Browse Files" link' do
- click_link('History')
+ it "shows the `Browse Files` link" do
+ click_link("History")
- expect(page).to have_link('Browse Files')
- expect(page).not_to have_link('Browse Directory')
+ expect(page).to have_link("Browse Files").and have_no_link("Browse Directory")
end
- it 'redirects to the permalink URL' do
- click_link('.gitignore')
- click_link('Permalink')
+ it "redirects to the permalink URL" do
+ click_link(".gitignore")
+ click_link("Permalink")
permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore")
@@ -74,80 +70,180 @@ describe 'Projects > Files > User browses files' do
end
end
- context 'when browsing a specific ref' do
+ context "when browsing the `markdown` branch", :js do
+ context "when browsing the root" do
+ before do
+ visit(project_tree_path(project, "markdown"))
+ end
+
+ it "shows correct files and links" do
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown")
+ find("a", text: /^#id$/)["href"] == project_tree_url(project, "markdown", anchor: "#id")
+ find("a", text: %r{^/#id$})["href"] == project_tree_url(project, "markdown", anchor: "#id")
+ find("a", text: /^README.md#id$/)["href"] == project_blob_url(project, "markdown/README.md", anchor: "#id")
+ find("a", text: %r{^d/README.md#id$})["href"] == project_blob_url(project, "d/markdown/README.md", anchor: "#id")
+ # rubocop:enable Lint/Void
+
+ expect(current_path).to eq(project_tree_path(project, "markdown"))
+ expect(page).to have_content("README.md")
+ .and have_content("CHANGELOG")
+ .and have_content("Welcome to GitLab GitLab is a free project and repository management application")
+ .and have_link("GitLab API doc")
+ .and have_link("GitLab API website")
+ .and have_link("Rake tasks")
+ .and have_link("backup and restore procedure")
+ .and have_link("GitLab API doc directory")
+ .and have_link("Maintenance")
+ .and have_header_with_correct_id_and_link(2, "Application details", "application-details")
+ end
+
+ it "shows correct content of file" do
+ click_link("GitLab API doc")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/README.md"))
+ expect(page).to have_content("All API requests require authentication")
+ .and have_content("Contents")
+ .and have_link("Users")
+ .and have_link("Rake tasks")
+ .and have_header_with_correct_id_and_link(1, "GitLab API", "gitlab-api")
+
+ click_link("Users")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
+ expect(page).to have_content("Get a list of users.")
+
+ page.go_back
+
+ click_link("Rake tasks")
+
+ expect(current_path).to eq(project_tree_path(project, "markdown/doc/raketasks"))
+ expect(page).to have_content("backup_restore.md").and have_content("maintenance.md")
+
+ click_link("shop")
+ click_link("Maintenance")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/raketasks/maintenance.md"))
+ expect(page).to have_content("bundle exec rake gitlab:env:info RAILS_ENV=production")
+
+ click_link("shop")
+
+ page.within(".tree-table") do
+ click_link("README.md")
+ end
+
+ page.go_back
+
+ page.within(".tree-table") do
+ click_link("d")
+ end
+
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown/d")
+ # rubocop:enable Lint/Void
+
+ page.within(".tree-table") do
+ click_link("README.md")
+ end
+
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md")
+ # rubocop:enable Lint/Void
+ end
+
+ it "shows correct content of directory" do
+ click_link("GitLab API doc directory")
+
+ expect(current_path).to eq(project_tree_path(project, "markdown/doc/api"))
+ expect(page).to have_content("README.md").and have_content("users.md")
+
+ click_link("Users")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
+ expect(page).to have_content("List users").and have_content("Get a list of users.")
+ end
+ end
+ end
+
+ context "when browsing a specific ref" do
+ let(:ref) { project_tree_path(project, "6d39438") }
+
before do
- visit(tree_path_ref_6d39438)
+ visit(ref)
end
- it 'shows files from a repository for "6d39438"' do
- expect(current_path).to eq(tree_path_ref_6d39438)
- expect(page).to have_content('.gitignore')
- expect(page).to have_content('LICENSE')
+ it "shows files from a repository for `6d39438`" do
+ expect(current_path).to eq(ref)
+ expect(page).to have_content(".gitignore").and have_content("LICENSE")
end
- it 'shows files from a repository with apostroph in its name', :js do
- first('.js-project-refs-dropdown').click
+ it "shows files from a repository with apostroph in its name", :js do
+ first(".js-project-refs-dropdown").click
- page.within('.project-refs-form') do
+ page.within(".project-refs-form") do
click_link("'test'")
end
- expect(page).to have_selector('.dropdown-toggle-text', text: "'test'")
+ expect(page).to have_selector(".dropdown-toggle-text", text: "'test'")
visit(project_tree_path(project, "'test'"))
- expect(page).to have_css('.tree-commit-link', visible: true)
- expect(page).not_to have_content('Loading commit data...')
+ expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
end
- it 'shows the code with a leading dot in the directory', :js do
- first('.js-project-refs-dropdown').click
+ it "shows the code with a leading dot in the directory", :js do
+ first(".js-project-refs-dropdown").click
- page.within('.project-refs-form') do
- click_link('fix')
+ page.within(".project-refs-form") do
+ click_link("fix")
end
- visit(project_tree_path(project, 'fix/.testdir'))
+ visit(project_tree_path(project, "fix/.testdir"))
- expect(page).to have_css('.tree-commit-link', visible: true)
- expect(page).not_to have_content('Loading commit data...')
+ expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
end
- it 'does not show the permalink link' do
- click_link('.gitignore')
+ it "does not show the permalink link" do
+ click_link(".gitignore")
- expect(page).not_to have_link('permalink')
+ expect(page).not_to have_link("permalink")
end
end
- context 'when browsing a file content' do
+ context "when browsing a file content" do
before do
visit(tree_path_root_ref)
- click_link('.gitignore')
+
+ click_link(".gitignore")
end
- it 'shows a file content', :js do
- wait_for_requests
- expect(page).to have_content('*.rbc')
+ it "shows a file content", :js do
+ expect(page).to have_content("*.rbc")
end
- it 'is possible to blame' do
- click_link 'Blame'
+ it "is possible to blame" do
+ click_link("Blame")
- expect(page).to have_content "*.rb"
- expect(page).to have_content "Dmitriy Zaporozhets"
- expect(page).to have_content "Initial commit"
+ expect(page).to have_content("*.rb")
+ .and have_content("Dmitriy Zaporozhets")
+ .and have_content("Initial commit")
end
end
- context 'when browsing a raw file' do
+ context "when browsing a raw file" do
before do
- visit(project_blob_path(project, File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)))
+ path = File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)
+
+ visit(project_blob_path(project, path))
end
- it 'shows a raw file content' do
- click_link('Open raw')
- expect(source).to eq('') # Body is filled in by gitlab-workhorse
+ it "shows a raw file content" do
+ click_link("Open raw")
+
+ expect(source).to eq("") # Body is filled in by gitlab-workhorse
end
end
end
diff --git a/spec/features/projects/issues/user_toggles_subscription_spec.rb b/spec/features/projects/issues/user_toggles_subscription_spec.rb
index 117a614b980..c2b2a193682 100644
--- a/spec/features/projects/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/projects/issues/user_toggles_subscription_spec.rb
@@ -1,9 +1,9 @@
require "spec_helper"
describe "User toggles subscription", :js do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, author: user) }
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, author: user) }
before do
project.add_developer(user)
@@ -12,7 +12,7 @@ describe "User toggles subscription", :js do
visit(project_issue_path(project, issue))
end
- it "unsibscribes from issue" do
+ it "unsubscribes from issue" do
subscription_button = find(".js-issuable-subscribe-button")
# Check we're subscribed.
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 1f4eec0a317..3ac6ca4fc86 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -1,47 +1,8 @@
require 'spec_helper'
feature 'Projects > Members > Master manages access requests' do
- let(:user) { create(:user) }
- let(:master) { create(:user) }
- let(:project) { create(:project, :public, :access_requestable) }
-
- background do
- project.request_access(user)
- project.add_master(master)
- sign_in(master)
- end
-
- scenario 'master can see access requests' do
- visit project_project_members_path(project)
-
- expect_visible_access_request(project, user)
- end
-
- scenario 'master can grant access' do
- visit project_project_members_path(project)
-
- expect_visible_access_request(project, user)
-
- perform_enqueued_jobs { click_on 'Grant access' }
-
- expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
- expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.full_name} project was granted"
- end
-
- scenario 'master can deny access' do
- visit project_project_members_path(project)
-
- expect_visible_access_request(project, user)
-
- perform_enqueued_jobs { click_on 'Deny access' }
-
- expect(ActionMailer::Base.deliveries.last.to).to eq [user.notification_email]
- expect(ActionMailer::Base.deliveries.last.subject).to match "Access to the #{project.full_name} project was denied"
- end
-
- def expect_visible_access_request(project, user)
- expect(project.requesters.exists?(user_id: user)).to be_truthy
- expect(page).to have_content "Users requesting access to #{project.name} 1"
- expect(page).to have_content user.name
+ it_behaves_like 'Master manages access requests' do
+ let(:entity) { create(:project, :public, :access_requestable) }
+ let(:members_page_path) { project_project_members_path(entity) }
end
end
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 0fd28a5681c..342be1d2a9d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -1,21 +1,27 @@
require 'rails_helper'
describe 'Projects > Settings > LFS settings' do
- let(:admin) { create(:admin) }
let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :master }
context 'LFS enabled setting' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- sign_in(admin)
+ sign_in(user)
+ project.add_role(user, role)
end
- it 'displays the correct elements', :js do
- visit edit_project_path(project)
+ context 'for master' do
+ let(:role) { :master }
- expect(page).to have_content('Git Large File Storage')
- expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ it 'displays the correct elements', :js do
+ visit edit_project_path(project)
+
+ expect(page).to have_content('Git Large File Storage')
+ expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ end
end
end
end
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 4a9d1cb87e1..fe6fa55fa75 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -1,6 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
-describe 'User creates wiki page' do
+describe "User creates wiki page" do
let(:user) { create(:user) }
before do
@@ -10,67 +10,104 @@ describe 'User creates wiki page' do
visit(project_wikis_path(project))
end
- context 'when wiki is empty' do
- context 'in a user namespace' do
+ context "when wiki is empty" do
+ context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) }
- it 'shows validation error message' do
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: '')
- click_on('Create page')
+ it "shows validation error message" do
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "")
+
+ click_on("Create page")
end
- expect(page).to have_content('The form contains the following error:')
- expect(page).to have_content("Content can't be blank")
+ expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "[link test](test)")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: '[link test](test)')
- click_on('Create page')
+ click_on("Create page")
end
- expect(page).to have_content('Home')
- expect(page).to have_content('link test')
+ expect(page).to have_content("Home").and have_content("link test")
- click_link('link test')
+ click_link("link test")
- expect(page).to have_content('Create Page')
+ expect(page).to have_content("Create Page")
end
- it 'shows non-escaped link in the pages list', :js do
- click_link('New page')
+ it "shows non-escaped link in the pages list", :js do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'one/two/three-test')
- click_on('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "one/two/three-test")
+
+ click_on("Create page")
end
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'wiki content')
- click_on('Create page')
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "wiki content")
+
+ click_on("Create page")
end
- expect(current_path).to include('one/two/three-test')
+ expect(current_path).to include("one/two/three-test")
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
- it 'has "Create home" as a commit message' do
- expect(page).to have_field('wiki[message]', with: 'Create home')
+ it "has `Create home` as a commit message" do
+ expect(page).to have_field("wiki[message]", with: "Create home")
end
- it 'creates a page from the home page' do
- fill_in(:wiki_content, with: 'My awesome wiki!')
+ it "creates a page from the home page" do
+ fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n")
+ fill_in(:wiki_message, with: "Adding links to wiki")
+
+ page.within(".wiki-form") do
+ click_button("Create page")
+ end
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+ expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
+ .and have_content("Home")
+ .and have_content("Last edited by #{user.name}")
+ .and have_header_with_correct_id_and_link(1, "Wiki header", "wiki-header")
+
+ click_link("test")
- page.within('.wiki-form') do
- click_button('Create page')
+ expect(current_path).to eq(project_wiki_path(project, "test"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Test").and have_content("Create Page")
+ end
+
+ click_link("Home")
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+
+ click_link("GitLab API")
+
+ expect(current_path).to eq(project_wiki_path(project, "api"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Create").and have_content("Api")
end
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ click_link("Home")
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+
+ click_link("Rake tasks")
+
+ expect(current_path).to eq(project_wiki_path(project, "raketasks"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Create").and have_content("Rake")
+ end
end
- it 'creates ASCII wiki with LaTeX blocks', :js do
- stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true)
+ it "creates ASCII wiki with LaTeX blocks", :js do
+ stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
ascii_content = <<~MD
:stem: latexmath
@@ -90,153 +127,164 @@ describe 'User creates wiki page' do
stem:[2+2] is 4
MD
- find('#wiki_format option[value=asciidoc]').select_option
+ find("#wiki_format option[value=asciidoc]").select_option
+
fill_in(:wiki_content, with: ascii_content)
- page.within('.wiki-form') do
- click_button('Create page')
+ page.within(".wiki-form") do
+ click_button("Create page")
end
- page.within '.wiki' do
- expect(page).to have_selector('.katex', count: 3)
- expect(page).to have_content('2+2 is 4')
+ page.within ".wiki" do
+ expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
end
end
end
- context 'in a group namespace', :js do
+ context "in a group namespace", :js do
let(:project) { create(:project, namespace: create(:group, :public)) }
- it 'has "Create home" as a commit message' do
- expect(page).to have_field('wiki[message]', with: 'Create home')
+ it "has `Create home` as a commit message" do
+ expect(page).to have_field("wiki[message]", with: "Create home")
end
- it 'creates a page from from the home page' do
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ it "creates a page from from the home page" do
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
+
+ click_button("Create page")
end
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Home")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
end
- context 'when wiki is not empty', :js do
+ context "when wiki is not empty", :js do
before do
- create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: 'home', content: 'Home page' })
+ create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
end
- context 'in a user namespace' do
+ context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) }
- context 'via the "new wiki page" page' do
- it 'creates a page with a single word' do
- click_link('New page')
+ context "via the `new wiki page` page" do
+ it "creates a page with a single word" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'foo')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "foo")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create foo')
+ expect(page).to have_field("wiki[message]", with: "Create foo")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Foo')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Foo")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
- it 'creates a page with spaces in the name' do
- click_link('New page')
+ it "creates a page with spaces in the name" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'Spaces in the name')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "Spaces in the name")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create spaces in the name')
+ expect(page).to have_field("wiki[message]", with: "Create spaces in the name")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Spaces in the name')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Spaces in the name")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
- it 'creates a page with hyphens in the name' do
- click_link('New page')
+ it "creates a page with hyphens in the name" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'hyphens-in-the-name')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "hyphens-in-the-name")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name')
+ expect(page).to have_field("wiki[message]", with: "Create hyphens in the name")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Hyphens in the name')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Hyphens in the name")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
- it 'shows the autocompletion dropdown' do
- click_link('New page')
+ it "shows the autocompletion dropdown" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'test-autocomplete')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "test-autocomplete")
+
+ click_button("Create page")
end
- page.within('.wiki-form') do
- find('#wiki_content').native.send_keys('')
- fill_in(:wiki_content, with: '@')
+ page.within(".wiki-form") do
+ find("#wiki_content").native.send_keys("")
+
+ fill_in(:wiki_content, with: "@")
end
- expect(page).to have_selector('.atwho-view')
+ expect(page).to have_selector(".atwho-view")
end
end
- context 'in a group namespace' do
+ context "in a group namespace" do
let(:project) { create(:project, namespace: create(:group, :public)) }
- context 'via the "new wiki page" page' do
- it 'creates a page' do
- click_link('New page')
+ context "via the `new wiki page` page" do
+ it "creates a page" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'foo')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "foo")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create foo')
+ expect(page).to have_field("wiki[message]", with: "Create foo")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Foo')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Foo")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
end
diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb
new file mode 100644
index 00000000000..631d7e3bced
--- /dev/null
+++ b/spec/features/users/active_sessions_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+feature 'Active user sessions', :clean_gitlab_redis_shared_state do
+ scenario 'Successful login adds a new active user login' do
+ now = Time.zone.parse('2018-03-12 09:06')
+ Timecop.freeze(now) do
+ user = create(:user)
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ sessions = ActiveSession.list(user)
+ expect(sessions.count).to eq 1
+
+ # refresh the current page updates the updated_at
+ Timecop.freeze(now + 1.minute) do
+ visit current_path
+
+ sessions = ActiveSession.list(user)
+ expect(sessions.first).to have_attributes(
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:07')
+ )
+ end
+ end
+ end
+
+ scenario 'Successful login cleans up obsolete entries' do
+ user = create(:user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ gitlab_sign_in(user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d'
+ end
+ end
+
+ scenario 'Sessionless login does not clean up obsolete entries' do
+ user = create(:user)
+ personal_access_token = create(:personal_access_token, user: user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ visit user_path(user, :atom, private_token: personal_access_token.token)
+ expect(page.status_code).to eq 200
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d'
+ end
+ end
+
+ scenario 'Logout deletes the active user login' do
+ user = create(:user)
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ expect(ActiveSession.list(user).count).to eq 1
+
+ gitlab_sign_out
+ expect(current_path).to eq new_user_session_path
+
+ expect(ActiveSession.list(user)).to be_empty
+ end
+end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index abc470788e1..16c0d418d98 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -2,43 +2,71 @@ require 'spec_helper'
describe GroupsFinder do
describe '#execute' do
- let(:user) { create(:user) }
-
- context 'root level groups' do
- let!(:private_group) { create(:group, :private) }
- let!(:internal_group) { create(:group, :internal) }
- let!(:public_group) { create(:group, :public) }
-
- context 'without a user' do
- subject { described_class.new.execute }
-
- it { is_expected.to eq([public_group]) }
+ let(:user) { create(:user) }
+
+ describe 'root level groups' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_type, :params, :results) do
+ nil | { all_available: true } | %i(public_group user_public_group)
+ nil | { all_available: false } | %i(public_group user_public_group)
+ nil | {} | %i(public_group user_public_group)
+
+ :regular | { all_available: true } | %i(public_group internal_group user_public_group user_internal_group
+ user_private_group)
+ :regular | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :regular | {} | %i(public_group internal_group user_public_group user_internal_group user_private_group)
+
+ :external | { all_available: true } | %i(public_group user_public_group user_internal_group user_private_group)
+ :external | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :external | {} | %i(public_group user_public_group user_internal_group user_private_group)
+
+ :admin | { all_available: true } | %i(public_group internal_group private_group user_public_group
+ user_internal_group user_private_group)
+ :admin | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :admin | {} | %i(public_group internal_group private_group user_public_group user_internal_group
+ user_private_group)
end
- context 'with a user' do
- subject { described_class.new(user).execute }
-
- context 'normal user' do
- it { is_expected.to contain_exactly(public_group, internal_group) }
- end
-
- context 'external user' do
- let(:user) { create(:user, external: true) }
-
- it { is_expected.to contain_exactly(public_group) }
+ with_them do
+ before do
+ # Fixme: Because of an issue: https://github.com/tomykaira/rspec-parameterized/issues/8#issuecomment-381888428
+ # The groups need to be created here, not with let syntax, and also compared by name and not ids
+
+ @groups = {
+ private_group: create(:group, :private, name: 'private_group'),
+ internal_group: create(:group, :internal, name: 'internal_group'),
+ public_group: create(:group, :public, name: 'public_group'),
+
+ user_private_group: create(:group, :private, name: 'user_private_group'),
+ user_internal_group: create(:group, :internal, name: 'user_internal_group'),
+ user_public_group: create(:group, :public, name: 'user_public_group')
+ }
+
+ if user_type
+ user =
+ case user_type
+ when :regular
+ create(:user)
+ when :external
+ create(:user, external: true)
+ when :admin
+ create(:user, :admin)
+ end
+ @groups.values_at(:user_private_group, :user_internal_group, :user_public_group).each do |group|
+ group.add_developer(user)
+ end
+ end
end
- context 'user is member of the private group' do
- before do
- private_group.add_guest(user)
- end
+ subject { described_class.new(User.last, params).execute.to_a }
- it { is_expected.to contain_exactly(public_group, internal_group, private_group) }
- end
+ it { is_expected.to match_array(@groups.values_at(*results)) }
end
end
context 'subgroups', :nested_groups do
+ let(:user) { create(:user) }
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json
index 4c4ca3b582f..0f9c221fd6d 100644
--- a/spec/fixtures/api/schemas/public_api/v4/notes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/notes.json
@@ -24,7 +24,10 @@
"system": { "type": "boolean" },
"noteable_id": { "type": "integer" },
"noteable_iid": { "type": "integer" },
- "noteable_type": { "type": "string" }
+ "noteable_type": { "type": "string" },
+ "resolved": { "type": "boolean" },
+ "resolvable": { "type": "boolean" },
+ "resolved_by": { "type": ["string", "null"] }
},
"required": [
"id", "body", "attachment", "author", "created_at", "updated_at",
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 43cb0dfe163..5e454f8b310 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -2,8 +2,6 @@
require 'spec_helper'
describe ApplicationHelper do
- include UploadHelpers
-
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -54,143 +52,6 @@ describe ApplicationHelper do
end
end
- describe 'project_icon' do
- it 'returns an url for the avatar' do
- project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
-
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
- end
-
- describe 'avatar_icon_for' do
- let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
- let(:email) { 'foo@example.com' }
- let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
-
- it 'prefers the user to retrieve the avatar_url' do
- expect(helper.avatar_icon_for(user, email).to_s)
- .to eq(user.avatar.url)
- end
-
- it 'falls back to email lookup if no user given' do
- expect(helper.avatar_icon_for(nil, email).to_s)
- .to eq(another_user.avatar.url)
- end
- end
-
- describe 'avatar_icon_for_email' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'using an email' do
- context 'when there is a matching user' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_email(user.email).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'when no user exists for the email' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
-
- helper.avatar_icon_for_email('foo@example.com', 20, 2)
- end
- end
-
- context 'without an email passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_email(nil, 20, 2)
- end
- end
- end
- end
-
- describe 'avatar_icon_for_user' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'with a user object passed' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_user(user).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'without a user object passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_user(nil, 20, 2)
- end
- end
- end
-
- describe 'gravatar_icon' do
- let(:user_email) { 'user@email.com' }
-
- context 'with Gravatar disabled' do
- before do
- stub_application_setting(gravatar_enabled?: false)
- end
-
- it 'returns a generic avatar' do
- expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
- end
- end
-
- context 'with Gravatar enabled' do
- before do
- stub_application_setting(gravatar_enabled?: true)
- end
-
- it 'returns a generic avatar when email is blank' do
- expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
- end
-
- it 'returns a valid Gravatar URL' do
- stub_config_setting(https: false)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'uses HTTPs when configured' do
- stub_config_setting(https: true)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://secure.gravatar.com')
- end
-
- it 'returns custom gravatar path when gravatar_url is set' do
- stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
-
- expect(gravatar_icon(user_email, 20))
- .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'accepts a custom size argument' do
- expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
- end
-
- it 'defaults size to 40@2x when given an invalid size' do
- expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
- end
-
- it 'accepts a scaling factor' do
- expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
- end
-
- it 'ignores case and surrounding whitespace' do
- normal = helper.gravatar_icon('foo@example.com')
- upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
-
- expect(normal).to eq upcase
- end
- end
- end
-
describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' }
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index c94fedd615b..120b23e66ac 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -18,6 +18,30 @@ describe AuthHelper do
end
end
+ describe "providers_for_base_controller" do
+ it 'returns all enabled providers from devise' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ expect(helper.providers_for_base_controller).to include(*[:twitter, :github])
+ end
+
+ it 'excludes ldap providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.providers_for_base_controller).not_to include(:ldapmain)
+ end
+ end
+
+ describe "form_based_providers" do
+ it 'includes LDAP providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.form_based_providers).to eq %i(ldapmain)
+ end
+
+ it 'includes crowd provider' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :crowd] }
+ expect(helper.form_based_providers).to eq %i(crowd)
+ end
+ end
+
describe 'enabled_button_based_providers' do
before do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 04c6d259135..5856bccb5b8 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -1,10 +1,147 @@
require 'rails_helper'
describe AvatarsHelper do
- include ApplicationHelper
+ include UploadHelpers
let(:user) { create(:user) }
+ describe '#project_icon' do
+ it 'returns an url for the avatar' do
+ project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
+
+ expect(helper.project_icon(project.full_path).to_s)
+ .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ end
+ end
+
+ describe '#avatar_icon_for' do
+ let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
+ let(:email) { 'foo@example.com' }
+ let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
+
+ it 'prefers the user to retrieve the avatar_url' do
+ expect(helper.avatar_icon_for(user, email).to_s)
+ .to eq(user.avatar.url)
+ end
+
+ it 'falls back to email lookup if no user given' do
+ expect(helper.avatar_icon_for(nil, email).to_s)
+ .to eq(another_user.avatar.url)
+ end
+ end
+
+ describe '#avatar_icon_for_email' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'using an email' do
+ context 'when there is a matching user' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_email(user.email).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'when no user exists for the email' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
+
+ helper.avatar_icon_for_email('foo@example.com', 20, 2)
+ end
+ end
+
+ context 'without an email passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_email(nil, 20, 2)
+ end
+ end
+ end
+ end
+
+ describe '#avatar_icon_for_user' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'with a user object passed' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_user(user).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'without a user object passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_user(nil, 20, 2)
+ end
+ end
+ end
+
+ describe '#gravatar_icon' do
+ let(:user_email) { 'user@email.com' }
+
+ context 'with Gravatar disabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: false)
+ end
+
+ it 'returns a generic avatar' do
+ expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
+ end
+ end
+
+ context 'with Gravatar enabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: true)
+ end
+
+ it 'returns a generic avatar when email is blank' do
+ expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
+ end
+
+ it 'returns a valid Gravatar URL' do
+ stub_config_setting(https: false)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'uses HTTPs when configured' do
+ stub_config_setting(https: true)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://secure.gravatar.com')
+ end
+
+ it 'returns custom gravatar path when gravatar_url is set' do
+ stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
+
+ expect(gravatar_icon(user_email, 20))
+ .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'accepts a custom size argument' do
+ expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
+ end
+
+ it 'defaults size to 40@2x when given an invalid size' do
+ expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
+ end
+
+ it 'accepts a scaling factor' do
+ expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
+ end
+
+ it 'ignores case and surrounding whitespace' do
+ normal = helper.gravatar_icon('foo@example.com')
+ upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
+
+ expect(normal).to eq upcase
+ end
+ end
+ end
+
describe '#user_avatar' do
subject { helper.user_avatar(user: user) }
diff --git a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
index 5df2d46ca6b..16d0b354a30 100644
--- a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
@@ -11,7 +11,6 @@ describe('IDE commit panel empty state', () => {
const Component = Vue.extend(emptyState);
Vue.set(store.state, 'noChangesStateSvgPath', 'no-changes');
- Vue.set(store.state, 'committedStateSvgPath', 'committed-state');
vm = createComponentWithStore(Component, store);
@@ -24,35 +23,7 @@ describe('IDE commit panel empty state', () => {
resetStore(vm.$store);
});
- describe('statusSvg', () => {
- it('uses noChangesStateSvgPath when commit message is empty', () => {
- expect(vm.statusSvg).toBe('no-changes');
- expect(vm.$el.querySelector('img').getAttribute('src')).toBe('no-changes');
- });
-
- it('uses committedStateSvgPath when commit message exists', done => {
- vm.$store.state.lastCommitMsg = 'testing';
-
- Vue.nextTick(() => {
- expect(vm.statusSvg).toBe('committed-state');
- expect(vm.$el.querySelector('img').getAttribute('src')).toBe('committed-state');
-
- done();
- });
- });
- });
-
it('renders no changes text when last commit message is empty', () => {
expect(vm.$el.textContent).toContain('No changes');
});
-
- it('renders last commit message when it exists', done => {
- vm.$store.state.lastCommitMsg = 'testing commit message';
-
- Vue.nextTick(() => {
- expect(vm.$el.textContent).toContain('testing commit message');
-
- done();
- });
- });
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js b/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js
new file mode 100644
index 00000000000..e1a432b81be
--- /dev/null
+++ b/spec/javascripts/ide/components/commit_sidebar/success_message_spec.js
@@ -0,0 +1,35 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import successMessage from '~/ide/components/commit_sidebar/success_message.vue';
+import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { resetStore } from '../../helpers';
+
+describe('IDE commit panel successful commit state', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(successMessage);
+
+ vm = createComponentWithStore(Component, store, {
+ committedStateSvgPath: 'committed-state',
+ });
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders last commit message when it exists', done => {
+ vm.$store.state.lastCommitMsg = 'testing commit message';
+
+ Vue.nextTick(() => {
+ expect(vm.$el.textContent).toContain('testing commit message');
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index ce5c525bed7..3ee11bd2f03 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -398,6 +398,20 @@ describe('IDE store file actions', () => {
})
.catch(done.fail);
});
+
+ it('bursts unused seal', done => {
+ store
+ .dispatch('changeFileContent', {
+ path: tmpFile.path,
+ content: 'content',
+ })
+ .then(() => {
+ expect(store.state.unusedSeal).toBe(false);
+
+ done();
+ })
+ .catch(done.fail);
+ });
});
describe('discardFileChanges', () => {
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index ede5e5e18e5..972713c5ad2 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -138,4 +138,14 @@ describe('Multi-file store mutations', () => {
expect(localState.fileFindVisible).toBe(true);
});
});
+
+ describe('BURST_UNUSED_SEAL', () => {
+ it('updates unusedSeal', () => {
+ expect(localState.unusedSeal).toBe(true);
+
+ mutations.BURST_UNUSED_SEAL(localState);
+
+ expect(localState.unusedSeal).toBe(false);
+ });
+ });
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index ae00fb76714..eab5c24406a 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -75,6 +75,14 @@ describe('text_utility', () => {
'This is a text with html .',
);
});
+
+ it('passes through with null string input', () => {
+ expect(textUtils.stripHtml(null, ' ')).toEqual(null);
+ });
+
+ it('passes through with undefined string input', () => {
+ expect(textUtils.stripHtml(undefined, ' ')).toEqual(undefined);
+ });
});
describe('convertToCamelCase', () => {
diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js
index 8b6e8b24f00..fcd7bea3f6d 100644
--- a/spec/javascripts/sidebar/mock_data.js
+++ b/spec/javascripts/sidebar/mock_data.js
@@ -138,7 +138,7 @@ const RESPONSE_MAP = {
},
{
id: 20,
- name_with_namespace: 'foo / bar',
+ name_with_namespace: '<img src=x onerror=alert(document.domain)> foo / bar',
},
],
},
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index a3fb965fbab..00847df4b60 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -69,6 +69,15 @@ describe('SidebarMoveIssue', function () {
expect($.fn.glDropdown).toHaveBeenCalled();
});
+
+ it('escapes html from project name', (done) => {
+ this.$toggleButton.dropdown('toggle');
+
+ setTimeout(() => {
+ expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; foo / bar');
+ done();
+ });
+ });
});
describe('onConfirmClicked', () => {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index bcd15f5eae2..2411d33a496 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -84,21 +84,11 @@ beforeEach(() => {
const axiosDefaultAdapter = getDefaultAdapter();
-let testFiles = process.env.TEST_FILES || [];
-if (testFiles.length > 0) {
- testFiles = testFiles.map(path => path.replace(/^spec\/javascripts\//, '').replace(/\.js$/, ''));
- console.log(`Running only tests matching: ${testFiles}`);
-} else {
- console.log('Running all tests');
-}
-
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function(path) {
try {
- if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
- testsContext(path);
- }
+ testsContext(path);
} catch (err) {
console.error('[ERROR] Unable to load spec: ', path);
describe('Test bundle', function() {
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
index 98ab61a0367..cea603368bf 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
-import wipComponent from '~/vue_merge_request_widget/components/states/mr_widget_wip';
+import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
const createComponent = () => {
- const Component = Vue.extend(wipComponent);
+ const Component = Vue.extend(WorkInProgress);
const mr = {
title: 'The best MR ever',
removeWIPPath: '/path/to/remove/wip',
@@ -17,10 +17,10 @@ const createComponent = () => {
});
};
-describe('MRWidgetWIP', () => {
+describe('Wip', () => {
describe('props', () => {
it('should have props', () => {
- const { mr, service } = wipComponent.props;
+ const { mr, service } = WorkInProgress.props;
expect(mr.type instanceof Object).toBeTruthy();
expect(mr.required).toBeTruthy();
diff --git a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
index 1fd145116df..068cdc85a07 100644
--- a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
@@ -47,16 +47,36 @@ describe Banzai::Filter::CommitTrailersFilter do
)
end
- it 'non GitLab users and replaces them with mailto links' do
- _, message_html = build_commit_message(
- trailer: trailer,
- name: FFaker::Name.name,
- email: email
- )
+ context 'non GitLab users' do
+ shared_examples 'mailto links' do
+ it 'replaces them with mailto links' do
+ _, message_html = build_commit_message(
+ trailer: trailer,
+ name: FFaker::Name.name,
+ email: email
+ )
- doc = filter(message_html)
+ doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
+ end
+ end
+
+ context 'when Gravatar is disabled' do
+ before do
+ stub_application_setting(gravatar_enabled: false)
+ end
+
+ it_behaves_like 'mailto links'
+ end
+
+ context 'when Gravatar is enabled' do
+ before do
+ stub_application_setting(gravatar_enabled: true)
+ end
+
+ it_behaves_like 'mailto links'
+ end
end
it 'multiple trailers in the same message' do
@@ -69,7 +89,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message)
expect_to_have_user_link_with_avatar(doc, user: user, trailer: trailer)
- expect_to_have_mailto_link(doc, email: email, trailer: different_trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: different_trailer)
end
context 'special names' do
@@ -90,7 +110,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
expect(doc.text).to match Regexp.escape(message)
end
end
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
new file mode 100644
index 00000000000..f8107dd40b9
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 20180420080616 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+ let(:jobs) { table(:ci_builds) }
+
+ before do
+ namespaces.create(id: 10, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 11, namespace_id: 10, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 12, project_id: 11, ref: 'master', sha: 'adf43c3a')
+
+ stages.create(id: 100, project_id: 11, pipeline_id: 12, name: 'build')
+ stages.create(id: 101, project_id: 11, pipeline_id: 12, name: 'test')
+
+ jobs.create!(id: 121, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 122, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 123, commit_id: 12, project_id: 11,
+ stage_idx: 10, stage_id: 100)
+ jobs.create!(id: 124, commit_id: 12, project_id: 11,
+ stage_idx: 3, stage_id: 101)
+ end
+
+ it 'correctly migrates stages indices' do
+ expect(stages.all.pluck(:position)).to all(be_nil)
+
+ described_class.new.perform(100, 101)
+
+ expect(stages.all.pluck(:position)).to eq [2, 3]
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index dc12ba076bc..0edc3f315bb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
context 'when pipeline is ready to be saved' do
before do
- pipeline.stages.build(name: 'test', project: project)
+ pipeline.stages.build(name: 'test', position: 0, project: project)
step.perform!
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index eb1b285c7bd..05ce3412fd8 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
describe '#attributes' do
it 'returns hash attributes of a stage' do
expect(subject.attributes).to be_a Hash
- expect(subject.attributes).to include(:name, :project)
+ expect(subject.attributes)
+ .to include(:name, :position, :pipeline, :project)
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index da1a6229ccf..9924641f829 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -234,59 +234,72 @@ describe Gitlab::Git::Repository, seed_helper: true do
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end
- shared_examples 'archive check' do |extenstion|
- it { expect(metadata['ArchivePath']).to match(%r{tmp/gitlab-git-test.git/gitlab-git-test-master-#{SeedRepo::LastCommit::ID}}) }
- it { expect(metadata['ArchivePath']).to end_with extenstion }
- end
+ describe '#archive_metadata' do
+ let(:storage_path) { '/tmp' }
+ let(:cache_key) { File.join(repository.gl_repository, SeedRepo::LastCommit::ID) }
- describe '#archive_prefix' do
- let(:project_name) { 'project-name'}
+ let(:append_sha) { true }
+ let(:ref) { 'master' }
+ let(:format) { nil }
- before do
- expect(repository).to receive(:name).once.and_return(project_name)
- end
+ let(:expected_extension) { 'tar.gz' }
+ let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" }
+ let(:expected_path) { File.join(storage_path, cache_key, expected_filename) }
+ let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" }
- it 'returns parameterised string for a ref containing slashes' do
- prefix = repository.archive_prefix('test/branch', 'SHA', append_sha: nil)
+ subject(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: append_sha) }
- expect(prefix).to eq("#{project_name}-test-branch-SHA")
+ it 'sets RepoPath to the repository path' do
+ expect(metadata['RepoPath']).to eq(repository.path)
end
- it 'returns correct string for a ref containing dots' do
- prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: nil)
-
- expect(prefix).to eq("#{project_name}-test.branch-SHA")
+ it 'sets CommitId to the commit SHA' do
+ expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID)
end
- it 'returns string with sha when append_sha is false' do
- prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: false)
-
- expect(prefix).to eq("#{project_name}-test.branch")
+ it 'sets ArchivePrefix to the expected prefix' do
+ expect(metadata['ArchivePrefix']).to eq(expected_prefix)
end
- end
- describe '#archive' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', append_sha: true) }
+ it 'sets ArchivePath to the expected globally-unique path' do
+ # This is really important from a security perspective. Think carefully
+ # before changing it: https://gitlab.com/gitlab-org/gitlab-ce/issues/45689
+ expect(expected_path).to include(File.join(repository.gl_repository, SeedRepo::LastCommit::ID))
- it_should_behave_like 'archive check', '.tar.gz'
- end
-
- describe '#archive_zip' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip', append_sha: true) }
+ expect(metadata['ArchivePath']).to eq(expected_path)
+ end
- it_should_behave_like 'archive check', '.zip'
- end
+ context 'append_sha varies archive path and filename' do
+ where(:append_sha, :ref, :expected_prefix) do
+ sha = SeedRepo::LastCommit::ID
- describe '#archive_bz2' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2', append_sha: true) }
+ true | 'master' | "gitlab-git-test-master-#{sha}"
+ true | sha | "gitlab-git-test-#{sha}-#{sha}"
+ false | 'master' | "gitlab-git-test-master"
+ false | sha | "gitlab-git-test-#{sha}"
+ nil | 'master' | "gitlab-git-test-master-#{sha}"
+ nil | sha | "gitlab-git-test-#{sha}"
+ end
- it_should_behave_like 'archive check', '.tar.bz2'
- end
+ with_them do
+ it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
+ it { expect(metadata['ArchivePath']).to eq(expected_path) }
+ end
+ end
- describe '#archive_fallback' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup', append_sha: true) }
+ context 'format varies archive path and filename' do
+ where(:format, :expected_extension) do
+ nil | 'tar.gz'
+ 'madeup' | 'tar.gz'
+ 'tbz2' | 'tar.bz2'
+ 'zip' | 'zip'
+ end
- it_should_behave_like 'archive check', '.tar.gz'
+ with_them do
+ it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
+ it { expect(metadata['ArchivePath']).to eq(expected_path) }
+ end
+ end
end
describe '#size' do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 31141807cb2..62da967cf96 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -232,6 +232,7 @@ Ci::Stage:
- id
- name
- status
+- position
- lock_version
- project_id
- pipeline_id
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 3cfdae794f6..7be8be54d5e 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -4,22 +4,10 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
let(:base_command) { described_class.new(application.name) }
- describe '#generate_script' do
- let(:helm_version) { Gitlab::Kubernetes::Helm::HELM_VERSION }
- let(:command) do
- <<~HEREDOC
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{helm_version}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- HEREDOC
- end
-
- subject { base_command.generate_script }
+ subject { base_command }
- it 'should return a command that prepares the environment for helm-cli' do
- expect(subject).to eq(command)
- end
+ it_behaves_like 'helm commands' do
+ let(:commands) { '' }
end
describe '#pod_resource' do
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index e6920b0a76f..89e36a298f8 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,23 +2,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:init_command) { described_class.new(application.name) }
+ let(:commands) { 'helm init >/dev/null' }
- describe '#generate_script' do
- let(:command) do
- <<~MSG.chomp
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init >/dev/null
- MSG
- end
+ subject { described_class.new(application.name) }
- subject { init_command.generate_script }
-
- it 'should return the appropriate command' do
- is_expected.to eq(command)
- end
- end
+ it_behaves_like 'helm commands'
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 137b8f718de..547f3f1752c 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -12,50 +12,36 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
)
end
- describe '#generate_script' do
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
-
- subject { install_command.generate_script }
+ subject { install_command }
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
+ end
- context 'with an application with a repository' do
- let(:ci_runner) { create(:ci_runner) }
- let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
- let(:install_command) do
- described_class.new(
- application.name,
- chart: application.chart,
- values: application.values,
- repository: application.repository
- )
- end
-
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
+ context 'with an application with a repository' do
+ let(:ci_runner) { create(:ci_runner) }
+ let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ let(:install_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ values: application.values,
+ repository: application.repository
+ )
+ end
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add #{application.name} #{application.repository}
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
end
end
diff --git a/spec/lib/omni_auth/strategies/jwt_spec.rb b/spec/lib/omni_auth/strategies/jwt_spec.rb
new file mode 100644
index 00000000000..23485fbcb18
--- /dev/null
+++ b/spec/lib/omni_auth/strategies/jwt_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Jwt do
+ include Rack::Test::Methods
+ include DeviseHelpers
+
+ context '.decoded' do
+ let(:strategy) { described_class.new({}) }
+ let(:timestamp) { Time.now.to_i }
+ let(:jwt_config) { Devise.omniauth_configs[:jwt] }
+ let(:key) { JWT.encode(claims, jwt_config.strategy.secret) }
+
+ let(:claims) do
+ {
+ id: 123,
+ name: "user_example",
+ email: "user@example.com",
+ iat: timestamp
+ }
+ end
+
+ before do
+ allow_any_instance_of(OmniAuth::Strategy).to receive(:options).and_return(jwt_config.strategy)
+ allow_any_instance_of(Rack::Request).to receive(:params).and_return({ 'jwt' => key })
+ end
+
+ it 'decodes the user information' do
+ result = strategy.decoded
+
+ expect(result["id"]).to eq(123)
+ expect(result["name"]).to eq("user_example")
+ expect(result["email"]).to eq("user@example.com")
+ expect(result["iat"]).to eq(timestamp)
+ end
+
+ context 'required claims is missing' do
+ let(:claims) do
+ {
+ id: 123,
+ email: "user@example.com",
+ iat: timestamp
+ }
+ end
+
+ it 'raises error' do
+ expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid)
+ end
+ end
+
+ context 'when valid_within is specified but iat attribute is missing in response' do
+ let(:claims) do
+ {
+ id: 123,
+ name: "user_example",
+ email: "user@example.com"
+ }
+ end
+
+ before do
+ jwt_config.strategy.valid_within = Time.now.to_i
+ end
+
+ it 'raises error' do
+ expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid)
+ end
+ end
+
+ context 'when timestamp claim is too skewed from present' do
+ let(:claims) do
+ {
+ id: 123,
+ name: "user_example",
+ email: "user@example.com",
+ iat: timestamp - 10.minutes.to_i
+ }
+ end
+
+ before do
+ jwt_config.strategy.valid_within = 2.seconds
+ end
+
+ it 'raises error' do
+ expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid)
+ end
+ end
+ end
+end
diff --git a/spec/migrations/schedule_stages_index_migration_spec.rb b/spec/migrations/schedule_stages_index_migration_spec.rb
new file mode 100644
index 00000000000..710264da375
--- /dev/null
+++ b/spec/migrations/schedule_stages_index_migration_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180420080616_schedule_stages_index_migration')
+
+describe ScheduleStagesIndexMigration, :sidekiq, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+
+ namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
+ stages.create!(id: 121, project_id: 123, pipeline_id: 1, name: 'build')
+ stages.create!(id: 122, project_id: 123, pipeline_id: 1, name: 'test')
+ stages.create!(id: 123, project_id: 123, pipeline_id: 1, name: 'deploy')
+ end
+
+ it 'schedules delayed background migrations in batches' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ expect(stages.all).to all(have_attributes(position: be_nil))
+
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 121, 121)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 122, 122)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 123, 123)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 3
+ end
+ end
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
new file mode 100644
index 00000000000..129b2f92683
--- /dev/null
+++ b/spec/models/active_session_spec.rb
@@ -0,0 +1,216 @@
+require 'rails_helper'
+
+RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
+ let(:user) do
+ create(:user).tap do |user|
+ user.current_sign_in_at = Time.current
+ end
+ end
+
+ let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') }
+
+ let(:request) do
+ double(:request, {
+ user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 ' \
+ '(KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]',
+ ip: '127.0.0.1',
+ session: session
+ })
+ end
+
+ describe '#current?' do
+ it 'returns true if the active session matches the current session' do
+ active_session = ActiveSession.new(session_id: '6919a6f1bb119dd7396fadc38fd18d0d')
+
+ expect(active_session.current?(session)).to be true
+ end
+
+ it 'returns false if the active session does not match the current session' do
+ active_session = ActiveSession.new(session_id: '59822c7d9fcdfa03725eff41782ad97d')
+
+ expect(active_session.current?(session)).to be false
+ end
+
+ it 'returns false if the session id is nil' do
+ active_session = ActiveSession.new(session_id: nil)
+ session = double(:session, id: nil)
+
+ expect(active_session.current?(session)).to be false
+ end
+ end
+
+ describe '.list' do
+ it 'returns all sessions by user' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
+ redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' }))
+ redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
+
+ redis.sadd(
+ "session:lookup:user:gitlab:#{user.id}",
+ %w[
+ 6919a6f1bb119dd7396fadc38fd18d0d
+ 59822c7d9fcdfa03725eff41782ad97d
+ ]
+ )
+ end
+
+ expect(ActiveSession.list(user)).to match_array [{ session_id: 'a' }, { session_id: 'b' }]
+ end
+
+ it 'does not return obsolete entries and cleans them up' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
+
+ redis.sadd(
+ "session:lookup:user:gitlab:#{user.id}",
+ %w[
+ 6919a6f1bb119dd7396fadc38fd18d0d
+ 59822c7d9fcdfa03725eff41782ad97d
+ ]
+ )
+ end
+
+ expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }]
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
+ end
+ end
+
+ it 'returns an empty array if the use does not have any active session' do
+ expect(ActiveSession.list(user)).to eq []
+ end
+ end
+
+ describe '.set' do
+ it 'sets a new redis entry for the user session and a lookup entry' do
+ ActiveSession.set(user, request)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each.to_a).to match_array [
+ "session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d",
+ "session:lookup:user:gitlab:#{user.id}"
+ ]
+ end
+ end
+
+ it 'adds timestamps and information from the request' do
+ Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
+ ActiveSession.set(user, request)
+
+ session = ActiveSession.list(user)
+
+ expect(session.count).to eq 1
+ expect(session.first).to have_attributes(
+ ip_address: '127.0.0.1',
+ browser: 'Mobile Safari',
+ os: 'iOS',
+ device_name: 'iPhone 6',
+ device_type: 'smartphone',
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:06'),
+ session_id: '6919a6f1bb119dd7396fadc38fd18d0d'
+ )
+ end
+ end
+
+ it 'keeps the created_at from the login on consecutive requests' do
+ now = Time.zone.parse('2018-03-12 09:06')
+
+ Timecop.freeze(now) do
+ ActiveSession.set(user, request)
+
+ Timecop.freeze(now + 1.minute) do
+ ActiveSession.set(user, request)
+
+ session = ActiveSession.list(user)
+
+ expect(session.first).to have_attributes(
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:07')
+ )
+ end
+ end
+ end
+ end
+
+ describe '.destroy' do
+ it 'removes the entry associated with the currently killed user session' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '')
+ redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [
+ "session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d",
+ "session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358"
+ ]
+ end
+ end
+
+ it 'removes the lookup entry' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
+ end
+ end
+
+ it 'removes the devise session' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
+ end
+ end
+
+ it 'does not remove the devise session if the active session could not be found' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ end
+
+ other_user = create(:user)
+
+ ActiveSession.destroy(other_user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:gitlab:*").to_a).not_to be_empty
+ end
+ end
+ end
+
+ describe '.cleanup' do
+ it 'removes obsolete lookup entries' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ ActiveSession.cleanup(user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
+ end
+ end
+
+ it 'does not bail if there are no lookup entries' do
+ ActiveSession.cleanup(user)
+ end
+ end
+end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 586d073eb5e..a00db1d2bfc 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -51,7 +51,7 @@ describe Ci::Stage, :models do
end
end
- describe 'update_status' do
+ describe '#update_status' do
context 'when stage objects needs to be updated' do
before do
create(:ci_build, :success, stage_id: stage.id)
@@ -87,4 +87,36 @@ describe Ci::Stage, :models do
end
end
end
+
+ describe '#index' do
+ context 'when stage has been imported and does not have position index set' do
+ before do
+ stage.update_column(:position, nil)
+ end
+
+ context 'when stage has statuses' do
+ before do
+ create(:ci_build, :running, stage_id: stage.id, stage_idx: 10)
+ end
+
+ it 'recalculates index before updating status' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 10
+ end
+ end
+
+ context 'when stage does not have statuses' do
+ it 'fallbacks to zero' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 0
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 2705421e540..fb51c0172ab 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -85,12 +85,35 @@ describe DiffNote do
end
describe "#diff_file" do
- it "returns the correct diff file" do
- diff_file = subject.diff_file
+ context 'when the discussion was created in the diff' do
+ it 'returns correct diff file' do
+ diff_file = subject.diff_file
- expect(diff_file.old_path).to eq(position.old_path)
- expect(diff_file.new_path).to eq(position.new_path)
- expect(diff_file.diff_refs).to eq(position.diff_refs)
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
+ end
+
+ context 'when discussion is outdated or not created in the diff' do
+ let(:diff_refs) { project.commit(sample_commit.id).diff_refs }
+ let(:position) do
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: diff_refs
+ )
+ end
+
+ it 'returns the correct diff file' do
+ diff_file = subject.diff_file
+
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
end
end
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index ba06ff42d87..6e35511e848 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -62,9 +62,7 @@ describe LfsObject do
.with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
.once
- lfs_object = create(:lfs_object)
- lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
- lfs_object.save!
+ create(:lfs_object, :with_file)
end
end
end
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 5a3b5b1f517..ffc78015f94 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -28,52 +28,12 @@ describe GroupMember do
end
end
- describe 'notifications' do
- describe "#after_create" do
- it "sends email to user" do
- membership = build(:group_member)
+ it_behaves_like 'members notifications', :group
- allow(membership).to receive(:notification_service)
- .and_return(double('NotificationService').as_null_object)
- expect(membership).to receive(:notification_service)
+ describe '#real_source_type' do
+ subject { create(:group_member).real_source_type }
- membership.save
- end
- end
-
- describe "#after_update" do
- before do
- @group_member = create :group_member
- allow(@group_member).to receive(:notification_service)
- .and_return(double('NotificationService').as_null_object)
- end
-
- it "sends email to user" do
- expect(@group_member).to receive(:notification_service)
- @group_member.update_attribute(:access_level, GroupMember::MASTER)
- end
-
- it "does not send an email when the access level has not changed" do
- expect(@group_member).not_to receive(:notification_service)
- @group_member.update_attribute(:access_level, GroupMember::OWNER)
- end
- end
-
- describe '#after_accept_request' do
- it 'calls NotificationService.accept_group_access_request' do
- member = create(:group_member, user: build(:user), requested_at: Time.now)
-
- expect_any_instance_of(NotificationService).to receive(:new_group_member)
-
- member.__send__(:after_accept_request)
- end
- end
-
- describe '#real_source_type' do
- subject { create(:group_member).real_source_type }
-
- it { is_expected.to eq 'Group' }
- end
+ it { is_expected.to eq 'Group' }
end
describe '#update_two_factor_requirement' do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index b8b0e63f92e..574eb468e4c 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -123,15 +123,5 @@ describe ProjectMember do
it { expect(@project_2.users).to be_empty }
end
- describe 'notifications' do
- describe '#after_accept_request' do
- it 'calls NotificationService.new_project_member' do
- member = create(:project_member, user: create(:user), requested_at: Time.now)
-
- expect_any_instance_of(NotificationService).to receive(:new_project_member)
-
- member.__send__(:after_accept_request)
- end
- end
- end
+ it_behaves_like 'members notifications', :project
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index becb146422e..5a9aa7c7d1b 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1213,7 +1213,7 @@ describe MergeRequest do
it 'enqueues MergeWorker job and updates merge_jid' do
merge_request = create(:merge_request)
user_id = double(:user_id)
- params = double(:params)
+ params = {}
merge_jid = 'hash-123'
expect(MergeWorker).to receive(:perform_async).with(merge_request.id, user_id, params) do
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 2a0d102d3fe..12681a147b4 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do
expect(notification_setting.new_issue).to eq(true)
expect(notification_setting.close_issue).to eq(true)
expect(notification_setting.merge_merge_request).to eq(true)
- expect(notification_setting.close_merge_request).to eq(false)
+
+ # In Rails 5 assigning a value which is not explicitly `true` or `false` ("nil" in this case)
+ # to a boolean column transforms it to `true`.
+ # In Rails 4 it transforms the value to `false` with deprecation warning.
+ # Replace `eq(Gitlab.rails5?)` with `eq(true)` when removing rails5? code.
+ expect(notification_setting.close_merge_request).to eq(Gitlab.rails5?)
expect(notification_setting.reopen_merge_request).to eq(false)
end
end
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index 4a44b219a67..ef34192f888 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -2,32 +2,53 @@ require 'spec_helper'
describe API::Discussions do
let(:user) { create(:user) }
- let!(:project) { create(:project, :public, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, :repository, namespace: user.namespace) }
let(:private_user) { create(:user) }
before do
- project.add_reporter(user)
+ project.add_developer(user)
end
- context "when noteable is an Issue" do
+ context 'when noteable is an Issue' do
let!(:issue) { create(:issue, project: project, author: user) }
let!(:issue_note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'issues', 'iid' do
+ it_behaves_like 'discussions API', 'projects', 'issues', 'iid' do
let(:parent) { project }
let(:noteable) { issue }
let(:note) { issue_note }
end
end
- context "when noteable is a Snippet" do
+ context 'when noteable is a Snippet' do
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'snippets', 'id' do
+ it_behaves_like 'discussions API', 'projects', 'snippets', 'id' do
let(:parent) { project }
let(:noteable) { snippet }
let(:note) { snippet_note }
end
end
+
+ context 'when noteable is a Merge Request' do
+ let!(:noteable) { create(:merge_request_with_diffs, source_project: project, target_project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'diff discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'resolvable discussions API', 'projects', 'merge_requests', 'iid'
+ end
+
+ context 'when noteable is a Commit' do
+ let!(:noteable) { create(:commit, project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'repository/commits', 'id'
+ it_behaves_like 'diff discussions API', 'projects', 'repository/commits', 'id'
+ end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 3ffdfdc0e9a..0a2963452e4 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -281,7 +281,7 @@ describe API::Jobs do
get_artifact_file(artifact)
expect(response).to have_gitlab_http_status(200)
- expect(response.headers)
+ expect(response.headers.to_h)
.to include('Content-Type' => 'application/json',
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
end
@@ -311,7 +311,7 @@ describe API::Jobs do
it 'returns specific job artifacts' do
expect(response).to have_gitlab_http_status(200)
- expect(response.headers).to include(download_headers)
+ expect(response.headers.to_h).to include(download_headers)
expect(response.body).to match_file(job.artifacts_file.file.file)
end
end
@@ -462,7 +462,7 @@ describe API::Jobs do
end
it { expect(response).to have_http_status(:ok) }
- it { expect(response.headers).to include(download_headers) }
+ it { expect(response.headers.to_h).to include(download_headers) }
end
context 'when artifacts are stored remotely' do
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 17c7a511857..6c1c13c9d80 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -1330,7 +1330,7 @@ describe API::Runner do
it 'download artifacts' do
expect(response).to have_http_status(200)
- expect(response.headers).to include download_headers
+ expect(response.headers.to_h).to include download_headers
end
end
@@ -1345,7 +1345,7 @@ describe API::Runner do
it 'uses workhorse send-url' do
expect(response).to have_gitlab_http_status(200)
- expect(response.headers).to include(
+ expect(response.headers.to_h).to include(
'Gitlab-Workhorse-Send-Data' => /send-url:/)
end
end
diff --git a/spec/requests/api/v3/builds_spec.rb b/spec/requests/api/v3/builds_spec.rb
index 00f067889a0..485d7c2cc43 100644
--- a/spec/requests/api/v3/builds_spec.rb
+++ b/spec/requests/api/v3/builds_spec.rb
@@ -232,7 +232,7 @@ describe API::V3::Builds do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
- expect(response.headers).to include(download_headers)
+ expect(response.headers.to_h).to include(download_headers)
expect(response.body).to match_file(build.artifacts_file.file.file)
end
end
@@ -332,7 +332,7 @@ describe API::V3::Builds do
end
it { expect(response).to have_http_status(200) }
- it { expect(response.headers).to include(download_headers) }
+ it { expect(response.headers.to_h).to include(download_headers) }
end
context 'when artifacts are stored remotely' do
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 6bed8e812c0..cd1a6cfc427 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -153,4 +153,13 @@ describe 'OpenID Connect requests' do
end
end
end
+
+ context 'OpenID configuration information' do
+ it 'correctly returns the configuration' do
+ get '/.well-known/openid-configuration'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to have_key('issuer')
+ end
+ end
end
diff --git a/spec/services/applications/create_service_spec.rb b/spec/services/applications/create_service_spec.rb
index 47a2a9d6403..9c43b56744b 100644
--- a/spec/services/applications/create_service_spec.rb
+++ b/spec/services/applications/create_service_spec.rb
@@ -1,13 +1,17 @@
-require 'spec_helper'
+require "spec_helper"
describe ::Applications::CreateService do
let(:user) { create(:user) }
let(:params) { attributes_for(:application) }
- let(:request) { ActionController::TestRequest.new(remote_ip: '127.0.0.1') }
+ let(:request) do
+ if Gitlab.rails5?
+ ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new)
+ else
+ ActionController::TestRequest.new(remote_ip: "127.0.0.1")
+ end
+ end
subject { described_class.new(user, params) }
- it 'creates an application' do
- expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1)
- end
+ it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) }
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 8de0bdf92e2..5bc6031388e 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) do
- Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test')
+ create(:ci_stage_entity, project: project,
+ pipeline: pipeline,
+ name: 'test')
end
let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index c38ddf4612b..e8568bf8bb3 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -219,7 +219,7 @@ describe MergeRequests::MergeService do
service.execute(merge_request)
- expect(merge_request.merge_error).to include(error_message)
+ expect(merge_request.merge_error).to include('Something went wrong during merge')
expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
@@ -231,7 +231,7 @@ describe MergeRequests::MergeService do
service.execute(merge_request)
- expect(merge_request.merge_error).to include(error_message)
+ expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook')
expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
diff --git a/spec/services/notes/resolve_service_spec.rb b/spec/services/notes/resolve_service_spec.rb
new file mode 100644
index 00000000000..b54d40a7a5c
--- /dev/null
+++ b/spec/services/notes/resolve_service_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Notes::ResolveService do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project) }
+ let(:user) { merge_request.author }
+
+ describe '#execute' do
+ it "resolves the note" do
+ described_class.new(merge_request.project, user).execute(note)
+ note.reload
+
+ expect(note.resolved?).to be true
+ expect(note.resolved_by).to eq(user)
+ end
+
+ it "sends notifications if all discussions are resolved" do
+ expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(merge_request)
+
+ described_class.new(merge_request.project, user).execute(note)
+ end
+ end
+end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 55bbe954491..48ef5f3c115 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -96,6 +96,37 @@ describe NotificationService, :mailer do
it_should_behave_like 'participating by assignee notification'
end
+ describe '#async' do
+ let(:async) { notification.async }
+ set(:key) { create(:personal_key) }
+
+ it 'returns an Async object with the correct parent' do
+ expect(async).to be_a(described_class::Async)
+ expect(async.parent).to eq(notification)
+ end
+
+ context 'when receiving a public method' do
+ it 'schedules a MailScheduler::NotificationServiceWorker' do
+ expect(MailScheduler::NotificationServiceWorker)
+ .to receive(:perform_async).with('new_key', key)
+
+ async.new_key(key)
+ end
+ end
+
+ context 'when receiving a private method' do
+ it 'raises NoMethodError' do
+ expect { async.notifiable?(key) }.to raise_error(NoMethodError)
+ end
+ end
+
+ context 'when recieving a non-existent method' do
+ it 'raises NoMethodError' do
+ expect { async.foo(key) }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
describe 'Keys' do
describe '#new_key' do
let(:key_options) { {} }
@@ -982,6 +1013,8 @@ describe NotificationService, :mailer do
let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' }
before do
+ project.add_master(merge_request.author)
+ project.add_master(merge_request.assignee)
build_team(merge_request.target_project)
add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
@@ -1093,15 +1126,18 @@ describe NotificationService, :mailer do
end
describe '#reassigned_merge_request' do
+ let(:current_user) { create(:user) }
+
before do
update_custom_notification(:reassign_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:reassign_merge_request, @u_custom_global)
end
it do
- notification.reassigned_merge_request(merge_request, merge_request.author)
+ notification.reassigned_merge_request(merge_request, current_user, merge_request.author)
should_email(merge_request.assignee)
+ should_email(merge_request.author)
should_email(@u_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
@@ -1116,7 +1152,7 @@ describe NotificationService, :mailer do
end
it 'adds "assigned" reason for new assignee' do
- notification.reassigned_merge_request(merge_request, merge_request.author)
+ notification.reassigned_merge_request(merge_request, current_user, merge_request.author)
email = find_email_for(merge_request.assignee)
@@ -1126,7 +1162,7 @@ describe NotificationService, :mailer do
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
- let(:notification_trigger) { notification.reassigned_merge_request(merge_request, @u_disabled) }
+ let(:notification_trigger) { notification.reassigned_merge_request(merge_request, current_user, merge_request.author) }
end
end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 1b6caeab15d..347ac13828c 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -29,25 +29,10 @@ describe Projects::UpdatePagesService do
end
describe 'pages artifacts' do
- context 'with expiry date' do
- before do
- build.artifacts_expire_in = "2 days"
- build.save!
- end
-
- it "doesn't delete artifacts" do
- expect(execute).to eq(:success)
-
- expect(build.reload.artifacts?).to eq(true)
- end
- end
-
- context 'without expiry date' do
- it "does delete artifacts" do
- expect(execute).to eq(:success)
+ it "doesn't delete artifacts after deploying" do
+ expect(execute).to eq(:success)
- expect(build.reload.artifacts?).to eq(false)
- end
+ expect(build.reload.artifacts?).to eq(true)
end
end
@@ -100,25 +85,10 @@ describe Projects::UpdatePagesService do
end
describe 'pages artifacts' do
- context 'with expiry date' do
- before do
- build.artifacts_expire_in = "2 days"
- build.save!
- end
-
- it "doesn't delete artifacts" do
- expect(execute).to eq(:success)
-
- expect(build.artifacts?).to eq(true)
- end
- end
-
- context 'without expiry date' do
- it "does delete artifacts" do
- expect(execute).to eq(:success)
+ it "doesn't delete artifacts after deploying" do
+ expect(execute).to eq(:success)
- expect(build.reload.artifacts?).to eq(false)
- end
+ expect(build.artifacts?).to eq(true)
end
end
@@ -153,11 +123,13 @@ describe Projects::UpdatePagesService do
expect(execute).not_to eq(:success)
end
- it 'fails for empty file fails' do
- build.job_artifacts_archive.update_attributes(file: empty_file)
+ context 'when using empty file' do
+ let(:file) { empty_file }
- expect { execute }
- .to raise_error(Projects::UpdatePagesService::FailedToExtractError)
+ it 'fails to extract' do
+ expect { execute }
+ .to raise_error(Projects::UpdatePagesService::FailedToExtractError)
+ end
end
context 'when timeout happens by DNS error' do
@@ -171,13 +143,12 @@ describe Projects::UpdatePagesService do
build.reload
expect(deploy_status).to be_failed
- expect(build.artifacts?).to be_truthy
end
end
context 'when failed to extract zip artifacts' do
before do
- allow_any_instance_of(described_class)
+ expect_any_instance_of(described_class)
.to receive(:extract_zip_archive!)
.and_raise(Projects::UpdatePagesService::FailedToExtractError)
end
@@ -188,21 +159,19 @@ describe Projects::UpdatePagesService do
build.reload
expect(deploy_status).to be_failed
- expect(build.artifacts?).to be_truthy
end
end
context 'when missing artifacts metadata' do
before do
- allow(build).to receive(:artifacts_metadata?).and_return(false)
+ expect(build).to receive(:artifacts_metadata?).and_return(false)
end
- it 'does not raise an error and remove artifacts as failed job' do
+ it 'does not raise an error as failed job' do
execute
build.reload
expect(deploy_status).to be_failed
- expect(build.artifacts?).to be_falsey
end
end
end
diff --git a/spec/services/repository_archive_clean_up_service_spec.rb b/spec/services/repository_archive_clean_up_service_spec.rb
index 2d7fa3f80f7..ab1c638fc39 100644
--- a/spec/services/repository_archive_clean_up_service_spec.rb
+++ b/spec/services/repository_archive_clean_up_service_spec.rb
@@ -1,15 +1,47 @@
require 'spec_helper'
describe RepositoryArchiveCleanUpService do
- describe '#execute' do
- subject(:service) { described_class.new }
+ subject(:service) { described_class.new }
+ describe '#execute (new archive locations)' do
+ let(:sha) { "0" * 40 }
+
+ it 'removes outdated archives and directories in a new-style path' do
+ in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
+ service.execute
+
+ files.each { |filename| expect(File.exist?(filename)).to be_falsy }
+ expect(File.directory?(dirname)).to be_falsy
+ expect(File.directory?(File.dirname(dirname))).to be_falsy
+ end
+ end
+
+ it 'does not remove directories when they contain outdated non-archives' do
+ in_directory_with_files("project-999/#{sha}", %w[tar conf rb], 2.hours) do |dirname, files|
+ service.execute
+
+ expect(File.directory?(dirname)).to be_truthy
+ end
+ end
+
+ it 'does not remove in-date archives in a new-style path' do
+ in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 1.hour) do |dirname, files|
+ service.execute
+
+ files.each { |filename| expect(File.exist?(filename)).to be_truthy }
+ end
+ end
+ end
+
+ describe '#execute (legacy archive locations)' do
context 'when the downloads directory does not exist' do
it 'does not remove any archives' do
path = '/invalid/path/'
stub_repository_downloads_path(path)
+ allow(File).to receive(:directory?).and_call_original
expect(File).to receive(:directory?).with(path).and_return(false)
+
expect(service).not_to receive(:clean_up_old_archives)
expect(service).not_to receive(:clean_up_empty_directories)
@@ -19,7 +51,7 @@ describe RepositoryArchiveCleanUpService do
context 'when the downloads directory exists' do
shared_examples 'invalid archive files' do |dirname, extensions, mtime|
- it 'does not remove files and directoy' do
+ it 'does not remove files and directory' do
in_directory_with_files(dirname, extensions, mtime) do |dir, files|
service.execute
@@ -43,7 +75,7 @@ describe RepositoryArchiveCleanUpService do
end
context 'with files older than 2 hours inside invalid directories' do
- it_behaves_like 'invalid archive files', 'john_doe/sample.git', %w[conf rb tar tar.gz], 2.hours
+ it_behaves_like 'invalid archive files', 'john/doe/sample.git', %w[conf rb tar tar.gz], 2.hours
end
context 'with files newer than 2 hours that matches valid archive extensions' do
@@ -58,24 +90,24 @@ describe RepositoryArchiveCleanUpService do
it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb tar tar.gz], 1.hour
end
end
+ end
- def in_directory_with_files(dirname, extensions, mtime)
- Dir.mktmpdir do |tmpdir|
- stub_repository_downloads_path(tmpdir)
- dir = File.join(tmpdir, dirname)
- files = create_temporary_files(dir, extensions, mtime)
+ def in_directory_with_files(dirname, extensions, mtime)
+ Dir.mktmpdir do |tmpdir|
+ stub_repository_downloads_path(tmpdir)
+ dir = File.join(tmpdir, dirname)
+ files = create_temporary_files(dir, extensions, mtime)
- yield(dir, files)
- end
+ yield(dir, files)
end
+ end
- def stub_repository_downloads_path(path)
- allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
- end
+ def stub_repository_downloads_path(path)
+ allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
+ end
- def create_temporary_files(dir, extensions, mtime)
- FileUtils.mkdir_p(dir)
- FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now - mtime)
- end
+ def create_temporary_files(dir, extensions, mtime)
+ FileUtils.mkdir_p(dir)
+ FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now - mtime)
end
end
diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb
index add359946db..efa317fd2f9 100644
--- a/spec/support/commit_trailers_spec_helper.rb
+++ b/spec/support/commit_trailers_spec_helper.rb
@@ -8,7 +8,7 @@ module CommitTrailersSpecHelper
expect(wrapper.attribute('data-user').value).to eq user.id.to_s
end
- def expect_to_have_mailto_link(doc, email:, trailer:)
+ def expect_to_have_mailto_link_with_avatar(doc, email:, trailer:)
wrapper = find_user_wrapper(doc, trailer)
expect_to_have_links_with_url_and_avatar(wrapper, "mailto:#{CGI.escape_html(email)}", email)
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 5b0b609f7f2..5a569d233bc 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -79,7 +79,7 @@ RSpec.shared_examples 'a creatable merge request' do
end
end
- it 'updates the branches when selecting a new target project' do
+ it 'updates the branches when selecting a new target project', :js do
target_project_member = target_project.owner
CreateBranchService.new(target_project, target_project_member)
.execute('a-brand-new-branch-to-test', 'master')
@@ -92,7 +92,7 @@ RSpec.shared_examples 'a creatable merge request' do
first('.js-target-branch').click
- within('.dropdown-target-branch .dropdown-content') do
+ within('.js-target-branch-dropdown .dropdown-content') do
expect(page).to have_content('a-brand-new-branch-to-test')
end
end
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
new file mode 100644
index 00000000000..b29bb3c2fc0
--- /dev/null
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -0,0 +1,52 @@
+RSpec.shared_examples 'Master manages access requests' do
+ let(:user) { create(:user) }
+ let(:master) { create(:user) }
+
+ before do
+ entity.request_access(user)
+ entity.respond_to?(:add_owner) ? entity.add_owner(master) : entity.add_master(master)
+ sign_in(master)
+ end
+
+ it 'master can see access requests' do
+ visit members_page_path
+
+ expect_visible_access_request(entity, user)
+ end
+
+ it 'master can grant access', :js do
+ visit members_page_path
+
+ expect_visible_access_request(entity, user)
+
+ accept_confirm { click_on 'Grant access' }
+
+ expect_no_visible_access_request(entity, user)
+
+ page.within('.members-list') do
+ expect(page).to have_content user.name
+ end
+ end
+
+ it 'master can deny access', :js do
+ visit members_page_path
+
+ expect_visible_access_request(entity, user)
+
+ accept_confirm { click_on 'Deny access' }
+
+ expect_no_visible_access_request(entity, user)
+ expect(page).not_to have_content user.name
+ end
+
+ def expect_visible_access_request(entity, user)
+ expect(entity.requesters.exists?(user_id: user)).to be_truthy
+ expect(page).to have_content "Users requesting access to #{entity.name} 1"
+ expect(page).to have_content user.name
+ end
+
+ def expect_no_visible_access_request(entity, user)
+ expect(entity.requesters.exists?(user_id: user)).to be_falsy
+ expect(page).not_to have_content "Users requesting access to #{entity.name}"
+ end
+end
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
new file mode 100644
index 00000000000..56e86a87ab9
--- /dev/null
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -0,0 +1,19 @@
+shared_examples 'helm commands' do
+ describe '#generate_script' do
+ let(:helm_setup) do
+ <<~EOS
+ set -eo pipefail
+ ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
+ echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+ EOS
+ end
+
+ it 'should return appropriate command' do
+ expect(subject.generate_script).to eq(helm_setup + commands)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
new file mode 100644
index 00000000000..76611e54306
--- /dev/null
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -0,0 +1,63 @@
+RSpec.shared_examples 'members notifications' do |entity_type|
+ let(:notification_service) { double('NotificationService').as_null_object }
+
+ before do
+ allow(member).to receive(:notification_service).and_return(notification_service)
+ end
+
+ describe "#after_create" do
+ let(:member) { build(:"#{entity_type}_member") }
+
+ it "sends email to user" do
+ expect(notification_service).to receive(:"new_#{entity_type}_member").with(member)
+
+ member.save
+ end
+ end
+
+ describe "#after_update" do
+ let(:member) { create(:"#{entity_type}_member", :developer) }
+
+ it "calls NotificationService.update_#{entity_type}_member" do
+ expect(notification_service).to receive(:"update_#{entity_type}_member").with(member)
+
+ member.update_attribute(:access_level, Member::MASTER)
+ end
+
+ it "does not send an email when the access level has not changed" do
+ expect(notification_service).not_to receive(:"update_#{entity_type}_member")
+
+ member.touch
+ end
+ end
+
+ describe '#accept_request' do
+ let(:member) { create(:"#{entity_type}_member", :access_request) }
+
+ it "calls NotificationService.new_#{entity_type}_member" do
+ expect(notification_service).to receive(:"new_#{entity_type}_member").with(member)
+
+ member.accept_request
+ end
+ end
+
+ describe "#accept_invite!" do
+ let(:member) { create(:"#{entity_type}_member", :invited) }
+
+ it "calls NotificationService.accept_#{entity_type}_invite" do
+ expect(notification_service).to receive(:"accept_#{entity_type}_invite").with(member)
+
+ member.accept_invite!(build(:user))
+ end
+ end
+
+ describe "#decline_invite!" do
+ let(:member) { create(:"#{entity_type}_member", :invited) }
+
+ it "calls NotificationService.decline_#{entity_type}_invite" do
+ expect(notification_service).to receive(:"decline_#{entity_type}_invite").with(member)
+
+ member.decline_invite!
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/diff_discussions.rb b/spec/support/shared_examples/requests/api/diff_discussions.rb
new file mode 100644
index 00000000000..85a4bd8ca27
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/diff_discussions.rb
@@ -0,0 +1,57 @@
+shared_examples 'diff discussions API' do |parent_type, noteable_type, id_name|
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "includes diff discussions" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user)
+
+ discussion = json_response.find { |record| record['id'] == diff_note.discussion_id }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(discussion).not_to be_nil
+ expect(discussion['individual_note']).to eq(false)
+ expect(discussion['notes'].first['body']).to eq(diff_note.note)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "returns a discussion by id" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions/#{diff_note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(diff_note.discussion_id)
+ expect(json_response['notes'].first['body']).to eq(diff_note.note)
+ expect(json_response['notes'].first['position']).to eq(diff_note.position.to_h.stringify_keys)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "creates a new diff note" do
+ position = diff_note.position.to_h
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['notes'].first['body']).to eq('hi!')
+ expect(json_response['notes'].first['type']).to eq('DiffNote')
+ expect(json_response['notes'].first['position']).to eq(position.stringify_keys)
+ end
+
+ it "returns a 400 bad request error when position is invalid" do
+ position = diff_note.position.to_h.merge(new_line: '100000')
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes" do
+ it 'adds a new note to the diff discussion' do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{diff_note.discussion_id}/notes", user), body: 'hi!'
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['type']).to eq('DiffNote')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions.rb b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
new file mode 100644
index 00000000000..408ad08cc48
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
@@ -0,0 +1,87 @@
+shared_examples 'resolvable discussions API' do |parent_type, noteable_type, id_name|
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "resolves discussion if resolved is true" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(true)
+ end
+
+ it "unresolves discussion if resolved is false" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: false
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(false)
+ end
+
+ it "returns a 400 bad request error if resolved parameter is not passed" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 401 unauthorized error if user is not authenticated" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}"), resolved: true
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+
+ it "returns a 403 error if user resolves discussion of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ context 'when user does not have access to read the discussion' do
+ before do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'responds with 404' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ it 'returns resolved note when resolved parameter is true' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['resolved']).to eq(true)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/12345", user),
+ body: 'Hello!'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 400 bad request error if neither body nor resolved parameter is given' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 403 error if user resolves note of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+end
diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb
index a2fb3886610..9f28510c3e4 100644
--- a/spec/uploaders/lfs_object_uploader_spec.rb
+++ b/spec/uploaders/lfs_object_uploader_spec.rb
@@ -46,8 +46,7 @@ describe LfsObjectUploader do
end
describe 'remote file' do
- let(:remote) { described_class::Store::REMOTE }
- let(:lfs_object) { create(:lfs_object, file_store: remote) }
+ let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) }
context 'with object storage enabled' do
before do
@@ -57,16 +56,11 @@ describe LfsObjectUploader do
it 'can store file remotely' do
allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
- store_file(lfs_object)
+ lfs_object
- expect(lfs_object.file_store).to eq remote
+ expect(lfs_object.file_store).to eq(described_class::Store::REMOTE)
expect(lfs_object.file.path).not_to be_blank
end
end
end
-
- def store_file(lfs_object)
- lfs_object.file = fixture_file_upload(Rails.root.join("spec/fixtures/dk.png"), "`/png")
- lfs_object.save!
- end
end
diff --git a/spec/workers/mail_scheduler/issue_due_worker_spec.rb b/spec/workers/mail_scheduler/issue_due_worker_spec.rb
index 48ac1b8a1a4..1026ae5b4bf 100644
--- a/spec/workers/mail_scheduler/issue_due_worker_spec.rb
+++ b/spec/workers/mail_scheduler/issue_due_worker_spec.rb
@@ -12,8 +12,8 @@ describe MailScheduler::IssueDueWorker do
create(:issue, :opened, project: project, due_date: 2.days.from_now) # due on another day
create(:issue, :opened, due_date: Date.tomorrow) # different project
- expect_any_instance_of(NotificationService).to receive(:issue_due).with(issue1)
- expect_any_instance_of(NotificationService).to receive(:issue_due).with(issue2)
+ expect(worker.notification_service).to receive(:issue_due).with(issue1)
+ expect(worker.notification_service).to receive(:issue_due).with(issue2)
worker.perform(project.id)
end
diff --git a/spec/workers/mail_scheduler/notification_service_worker_spec.rb b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
new file mode 100644
index 00000000000..f725c8763a0
--- /dev/null
+++ b/spec/workers/mail_scheduler/notification_service_worker_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe MailScheduler::NotificationServiceWorker do
+ let(:worker) { described_class.new }
+ let(:method) { 'new_key' }
+ set(:key) { create(:personal_key) }
+
+ def serialize(*args)
+ ActiveJob::Arguments.serialize(args)
+ end
+
+ describe '#perform' do
+ it 'deserializes arguments from global IDs' do
+ expect(worker.notification_service).to receive(method).with(key)
+
+ worker.perform(method, *serialize(key))
+ end
+
+ context 'when the arguments cannot be deserialized' do
+ it 'does nothing' do
+ expect(worker.notification_service).not_to receive(method)
+
+ worker.perform(method, key.to_global_id.to_s.succ)
+ end
+ end
+
+ context 'when the method is not a public method' do
+ it 'raises NoMethodError' do
+ expect { worker.perform('notifiable?', *serialize(key)) }.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ describe '.perform_async' do
+ it 'serializes arguments as global IDs when scheduling' do
+ Sidekiq::Testing.fake! do
+ described_class.perform_async(method, key)
+
+ expect(described_class.jobs.count).to eq(1)
+ expect(described_class.jobs.first).to include('args' => [method, *serialize(key)])
+ end
+ end
+ end
+end
diff --git a/spec/workers/namespaceless_project_destroy_worker_spec.rb b/spec/workers/namespaceless_project_destroy_worker_spec.rb
index 479d9396eca..eec110dfbfb 100644
--- a/spec/workers/namespaceless_project_destroy_worker_spec.rb
+++ b/spec/workers/namespaceless_project_destroy_worker_spec.rb
@@ -22,13 +22,11 @@ describe NamespacelessProjectDestroyWorker do
end
end
- # Only possible with schema 20180222043024 and lower.
- # Project#namespace_id has not null constraint since then
- context 'project has no namespace', :migration, schema: 20180222043024 do
- let!(:project) do
- project = build(:project, namespace_id: nil)
- project.save(validate: false)
- project
+ context 'project has no namespace' do
+ let!(:project) { create(:project) }
+
+ before do
+ allow_any_instance_of(Project).to receive(:namespace).and_return(nil)
end
context 'project not a fork of another project' do
@@ -61,8 +59,7 @@ describe NamespacelessProjectDestroyWorker do
let!(:parent_project) { create(:project) }
let(:project) do
namespaceless_project = fork_project(parent_project)
- namespaceless_project.namespace_id = nil
- namespaceless_project.save(validate: false)
+ namespaceless_project.save
namespaceless_project
end