summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples')
-rw-r--r--spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb37
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/access_tokens_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb46
-rw-r--r--spec/support/shared_examples/features/inviting_members_shared_examples.rb175
-rw-r--r--spec/support/shared_examples/features/project_upload_files_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb141
-rw-r--r--spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/graphql/notes_creation_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/helpers/wiki_helpers_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/incident_management/issuable_escalation_statuses/build_examples.rb20
-rw-r--r--spec/support/shared_examples/lib/gitlab/event_store_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/lib/wikis_api_examples.rb6
-rw-r--r--spec/support/shared_examples/models/application_setting_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb39
-rw-r--r--spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/group_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb61
-rw-r--r--spec/support/shared_examples/models/issuable_link_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb60
-rw-r--r--spec/support/shared_examples/models/project_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/models/wiki_shared_examples.rb140
-rw-r--r--spec/support/shared_examples/policies/wiki_policies_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb90
-rw-r--r--spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/issuable_participants_examples.rb30
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb79
-rw-r--r--spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb19
-rw-r--r--spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb94
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/views/milestone_shared_examples.rb78
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb70
43 files changed, 1103 insertions, 332 deletions
diff --git a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
index 1e303197990..15590fd10dc 100644
--- a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
+++ b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
@@ -138,7 +138,7 @@ RSpec.shared_examples 'multiple issue boards' do
wait_for_requests
- dropdown_selector = '.js-boards-selector .dropdown-menu'
+ dropdown_selector = '[data-testid="boards-selector"] .dropdown-menu'
page.within(dropdown_selector) do
yield
end
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index 46fc2cbdc9b..2ea98002de1 100644
--- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
@@ -184,6 +184,41 @@ RSpec.shared_examples 'a GitHub-ish import controller: GET status' do
expect(json_response.dig("provider_repos").count).to eq(1)
end
end
+
+ context 'when namespace_id query param is provided' do
+ let_it_be(:current_user) { create(:user) }
+
+ let(:namespace) { create(:namespace) }
+
+ before do
+ allow(controller).to receive(:current_user).and_return(current_user)
+ end
+
+ context 'when user is allowed to create projects in this namespace' do
+ before do
+ allow(current_user).to receive(:can?).and_return(true)
+ end
+
+ it 'provides namespace to the template' do
+ get :status, params: { namespace_id: namespace.id }, format: :html
+
+ expect(response).to have_gitlab_http_status :ok
+ expect(assigns(:namespace)).to eq(namespace)
+ end
+ end
+
+ context 'when user is not allowed to create projects in this namespace' do
+ before do
+ allow(current_user).to receive(:can?).and_return(false)
+ end
+
+ it 'renders 404' do
+ get :status, params: { namespace_id: namespace.id }, format: :html
+
+ expect(response).to have_gitlab_http_status :not_found
+ end
+ end
+ end
end
end
@@ -515,7 +550,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: GET realtime_changes' do
get :realtime_changes
- expect(json_response).to eq([{ "id" => project.id, "import_status" => project.import_status }])
+ expect(json_response).to match([a_hash_including({ "id" => project.id, "import_status" => project.import_status })])
expect(Integer(response.headers['Poll-Interval'])).to be > -1
end
end
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index bf26922d9c5..885c0229038 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -150,6 +150,7 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response).to render_template('shared/wikis/diff')
expect(assigns(:diffs)).to be_a(Gitlab::Diff::FileCollection::Base)
expect(assigns(:diff_notes_disabled)).to be(true)
+ expect(assigns(:page).content).to be_empty
end
end
@@ -475,9 +476,13 @@ RSpec.shared_examples 'wiki controller actions' do
context 'when page exists' do
shared_examples 'deletes the page' do
specify do
- expect do
- request
- end.to change { wiki.list_pages.size }.by(-1)
+ aggregate_failures do
+ expect do
+ request
+ end.to change { wiki.list_pages.size }.by(-1)
+
+ expect(assigns(:page).content).to be_empty
+ end
end
end
diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
index ae246a87bb6..215d9d3e5a8 100644
--- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb
+++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
@@ -29,15 +29,15 @@ RSpec.shared_examples 'resource access tokens creation' do |resource_type|
click_on '1'
# Scopes
- check 'api'
check 'read_api'
+ check 'read_repository'
click_on "Create #{resource_type} access token"
expect(active_resource_access_tokens).to have_text(name)
expect(active_resource_access_tokens).to have_text('in')
- expect(active_resource_access_tokens).to have_text('api')
expect(active_resource_access_tokens).to have_text('read_api')
+ expect(active_resource_access_tokens).to have_text('read_repository')
expect(active_resource_access_tokens).to have_text('Maintainer')
expect(created_resource_access_token).not_to be_empty
end
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index 2332285540a..5c44cb7f04b 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -1,14 +1,48 @@
# frozen_string_literal: true
RSpec.shared_examples 'edits content using the content editor' do
- it 'formats text as bold using bubble menu' do
- content_editor_testid = '[data-testid="content-editor"] [contenteditable]'
+ content_editor_testid = '[data-testid="content-editor"] [contenteditable].ProseMirror'
- expect(page).to have_css(content_editor_testid)
+ describe 'formatting bubble menu' do
+ it 'shows a formatting bubble menu for a regular paragraph' do
+ expect(page).to have_css(content_editor_testid)
- find(content_editor_testid).send_keys 'Typing text in the content editor'
- find(content_editor_testid).send_keys [:shift, :left]
+ find(content_editor_testid).send_keys 'Typing text in the content editor'
+ find(content_editor_testid).send_keys [:shift, :left]
- expect(page).to have_css('[data-testid="formatting-bubble-menu"]')
+ expect(page).to have_css('[data-testid="formatting-bubble-menu"]')
+ end
+
+ it 'does not show a formatting bubble menu for code' do
+ find(content_editor_testid).send_keys 'This is a `code`'
+ find(content_editor_testid).send_keys [:shift, :left]
+
+ expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
+ end
+ end
+
+ describe 'code block bubble menu' do
+ it 'shows a code block bubble menu for a code block' do
+ find(content_editor_testid).send_keys '```js ' # trigger input rule
+ find(content_editor_testid).send_keys 'var a = 0'
+ find(content_editor_testid).send_keys [:shift, :left]
+
+ expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
+ expect(page).to have_css('[data-testid="code-block-bubble-menu"]')
+ end
+
+ it 'sets code block type to "javascript" for `js`' do
+ find(content_editor_testid).send_keys '```js '
+ find(content_editor_testid).send_keys 'var a = 0'
+
+ expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Javascript')
+ end
+
+ it 'sets code block type to "Custom (nomnoml)" for `nomnoml`' do
+ find(content_editor_testid).send_keys '```nomnoml '
+ find(content_editor_testid).send_keys 'test'
+
+ expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Custom (nomnoml)')
+ end
end
end
diff --git a/spec/support/shared_examples/features/inviting_members_shared_examples.rb b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
new file mode 100644
index 00000000000..58357b262f5
--- /dev/null
+++ b/spec/support/shared_examples/features/inviting_members_shared_examples.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'inviting members' do |snowplow_invite_label|
+ before_all do
+ group.add_owner(user1)
+ end
+
+ it 'adds user as member', :js, :snowplow, :aggregate_failures do
+ visit members_page_path
+
+ invite_member(user2.name, role: 'Reporter')
+
+ page.within find_member_row(user2) do
+ expect(page).to have_button('Reporter')
+ end
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: snowplow_invite_label,
+ property: 'existing_user',
+ user: user1
+ )
+ end
+
+ it 'invites user by email', :js, :snowplow, :aggregate_failures do
+ visit members_page_path
+
+ invite_member('test@example.com', role: 'Reporter')
+
+ click_link 'Invited'
+
+ page.within find_invited_member_row('test@example.com') do
+ expect(page).to have_button('Reporter')
+ end
+
+ expect_snowplow_event(
+ category: 'Members::InviteService',
+ action: 'create_member',
+ label: snowplow_invite_label,
+ property: 'net_new_user',
+ user: user1
+ )
+ end
+
+ it 'invites user by username and invites user by email', :js, :aggregate_failures do
+ visit members_page_path
+
+ invite_member([user2.name, 'test@example.com'], role: 'Reporter')
+
+ page.within find_member_row(user2) do
+ expect(page).to have_button('Reporter')
+ end
+
+ click_link 'Invited'
+
+ page.within find_invited_member_row('test@example.com') do
+ expect(page).to have_button('Reporter')
+ end
+ end
+
+ context 'when member is already a member by username' do
+ it 'updates the member for that user', :js do
+ visit members_page_path
+
+ invite_member(user2.name, role: 'Developer')
+
+ invite_member(user2.name, role: 'Reporter', refresh: false)
+
+ expect(page).not_to have_selector(invite_modal_selector)
+
+ page.refresh
+
+ page.within find_invited_member_row(user2.name) do
+ expect(page).to have_button('Reporter')
+ end
+ end
+ end
+
+ context 'when member is already a member by email' do
+ it 'fails with an error', :js do
+ visit members_page_path
+
+ invite_member('test@example.com', role: 'Developer')
+
+ invite_member('test@example.com', role: 'Reporter', refresh: false)
+
+ expect(page).to have_selector(invite_modal_selector)
+ expect(page).to have_content("The member's email address has already been taken")
+
+ page.refresh
+
+ click_link 'Invited'
+
+ page.within find_invited_member_row('test@example.com') do
+ expect(page).to have_button('Developer')
+ end
+ end
+ end
+
+ context 'when inviting a parent group member to the sub-entity' do
+ before_all do
+ group.add_owner(user1)
+ group.add_developer(user2)
+ end
+
+ context 'when role is higher than parent group membership' do
+ let(:role) { 'Maintainer' }
+
+ it 'adds the user as a member on sub-entity with higher access level', :js do
+ visit subentity_members_page_path
+
+ invite_member(user2.name, role: role, refresh: false)
+
+ expect(page).not_to have_selector(invite_modal_selector)
+
+ page.refresh
+
+ page.within find_invited_member_row(user2.name) do
+ expect(page).to have_button(role)
+ end
+ end
+ end
+
+ context 'when role is lower than parent group membership' do
+ let(:role) { 'Reporter' }
+
+ it 'fails with an error', :js do
+ visit subentity_members_page_path
+
+ invite_member(user2.name, role: role, refresh: false)
+
+ expect(page).to have_selector(invite_modal_selector)
+ expect(page).to have_content "Access level should be greater than or equal to Developer inherited membership " \
+ "from group #{group.name}"
+
+ page.refresh
+
+ page.within find_invited_member_row(user2.name) do
+ expect(page).to have_content('Developer')
+ expect(page).not_to have_button('Developer')
+ end
+ end
+
+ context 'when there are multiple users invited with errors' do
+ let_it_be(:user3) { create(:user) }
+
+ before do
+ group.add_maintainer(user3)
+ end
+
+ it 'only shows the first user error', :js do
+ visit subentity_members_page_path
+
+ invite_member([user2.name, user3.name], role: role, refresh: false)
+
+ expect(page).to have_selector(invite_modal_selector)
+ expect(page).to have_text("Access level should be greater than or equal to", count: 1)
+
+ page.refresh
+
+ page.within find_invited_member_row(user2.name) do
+ expect(page).to have_content('Developer')
+ expect(page).not_to have_button('Developer')
+ end
+
+ page.within find_invited_member_row(user3.name) do
+ expect(page).to have_content('Maintainer')
+ expect(page).not_to have_button('Maintainer')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/project_upload_files_shared_examples.rb b/spec/support/shared_examples/features/project_upload_files_shared_examples.rb
index 066c3e17a09..0a5ad5a59c0 100644
--- a/spec/support/shared_examples/features/project_upload_files_shared_examples.rb
+++ b/spec/support/shared_examples/features/project_upload_files_shared_examples.rb
@@ -62,7 +62,7 @@ RSpec.shared_examples 'it uploads and commits a new image file' do |drop: false|
visit(project_blob_path(project, 'upload_image/logo_sample.svg'))
- expect(page).to have_css('.file-content img')
+ expect(page).to have_css('.file-holder img')
end
end
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
new file mode 100644
index 00000000000..d9460c7b8f1
--- /dev/null
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'shows and resets runner registration token' do
+ include Spec::Support::Helpers::ModalHelpers
+ include Spec::Support::Helpers::Features::RunnersHelpers
+
+ before do
+ click_on dropdown_text
+ end
+
+ describe 'shows registration instructions' do
+ before do
+ click_on 'Show runner installation and registration instructions'
+
+ wait_for_requests
+ end
+
+ it 'opens runner installation modal', :aggregate_failures do
+ within_modal do
+ expect(page).to have_text "Install a runner"
+ expect(page).to have_text "Environment"
+ expect(page).to have_text "Architecture"
+ expect(page).to have_text "Download and install binary"
+ end
+ end
+
+ it 'dismisses runner installation modal' do
+ within_modal do
+ click_button('Close', match: :first)
+ end
+
+ expect(page).not_to have_text "Install a runner"
+ end
+ end
+
+ it 'has a registration token' do
+ click_on 'Click to reveal'
+ expect(page.find('[data-testid="token-value"] input').value).to have_content(registration_token)
+ end
+
+ describe 'reset registration token' do
+ let!(:old_registration_token) { find('[data-testid="token-value"] input').value }
+
+ before do
+ click_on 'Reset registration token'
+
+ within_modal do
+ click_button('Reset token', match: :first)
+ end
+
+ wait_for_requests
+ end
+
+ it 'changes registration token' do
+ expect(find('.gl-toast')).to have_content('New registration token generated!')
+
+ click_on dropdown_text
+ click_on 'Click to reveal'
+
+ expect(old_registration_token).not_to eq registration_token
+ end
+ end
+end
+
+RSpec.shared_examples 'shows no runners' do
+ it 'shows counts with 0' do
+ expect(page).to have_text "Online runners 0"
+ expect(page).to have_text "Offline runners 0"
+ expect(page).to have_text "Stale runners 0"
+ end
+
+ it 'shows "no runners" message' do
+ expect(page).to have_text 'No runners found'
+ end
+end
+
+RSpec.shared_examples 'shows runner in list' do
+ it 'does not show empty state' do
+ expect(page).not_to have_content 'No runners found'
+ end
+
+ it 'shows runner row' do
+ within_runner_row(runner.id) do
+ expect(page).to have_text "##{runner.id}"
+ expect(page).to have_text runner.short_sha
+ expect(page).to have_text runner.description
+ end
+ end
+end
+
+RSpec.shared_examples 'pauses, resumes and deletes a runner' do
+ include Spec::Support::Helpers::ModalHelpers
+
+ it 'pauses and resumes runner' do
+ within_runner_row(runner.id) do
+ click_button "Pause"
+
+ expect(page).to have_text 'paused'
+ expect(page).to have_button 'Resume'
+ expect(page).not_to have_button 'Pause'
+
+ click_button "Resume"
+
+ expect(page).not_to have_text 'paused'
+ expect(page).not_to have_button 'Resume'
+ expect(page).to have_button 'Pause'
+ end
+ end
+
+ describe 'deletes runner' do
+ before do
+ within_runner_row(runner.id) do
+ click_on 'Delete runner'
+ end
+ end
+
+ it 'shows a confirmation modal' do
+ expect(page).to have_text "Delete runner ##{runner.id} (#{runner.short_sha})?"
+ expect(page).to have_text "Are you sure you want to continue?"
+ end
+
+ it 'deletes a runner' do
+ within_modal do
+ click_on 'Delete runner'
+ end
+
+ expect(page.find('.gl-toast')).to have_text(/Runner .+ deleted/)
+ expect(page).not_to have_content runner.description
+ end
+
+ it 'cancels runner deletion' do
+ within_modal do
+ click_on 'Cancel'
+ end
+
+ wait_for_requests
+
+ expect(page).to have_content runner.description
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
index bb5460e2a6f..095c48cade8 100644
--- a/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/search_timeouts_shared_examples.rb
@@ -11,6 +11,7 @@ RSpec.shared_examples 'search timeouts' do |scope|
end
it 'renders timeout information' do
+ # expect(page).to have_content('This endpoint has been requested too many times.')
expect(page).to have_content('Your search timed out')
end
diff --git a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
index f676b6aa60d..41b1964cff0 100644
--- a/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_creates_wiki_page_shared_examples.rb
@@ -20,6 +20,12 @@ RSpec.shared_examples 'User creates wiki page' do
click_link "Create your first page"
end
+ it 'shows all available formats in the dropdown' do
+ Wiki::VALID_USER_MARKUPS.each do |key, markup|
+ expect(page).to have_css("#wiki_format option[value=#{key}]", text: markup[:name])
+ end
+ end
+
it "disables the submit button", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
index 85490bffc0e..12a4c6d7583 100644
--- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
@@ -145,19 +145,6 @@ RSpec.shared_examples 'User updates wiki page' do
it_behaves_like 'edits content using the content editor'
end
-
- context 'with feature flag off' do
- before do
- stub_feature_flags(wiki_switch_between_content_editor_raw_markdown: false)
- visit(wiki_path(wiki))
-
- click_link('Edit')
-
- click_button 'Use the new editor'
- end
-
- it_behaves_like 'edits content using the content editor'
- end
end
end
diff --git a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
index 2e3a3ce6b41..04bb2fb69bb 100644
--- a/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/boards_create_shared_examples.rb
@@ -3,16 +3,6 @@
RSpec.shared_examples 'boards create mutation' do
include GraphqlHelpers
- let_it_be(:current_user, reload: true) { create(:user) }
- let(:name) { 'board name' }
- let(:mutation) { graphql_mutation(:create_board, params) }
-
- subject { post_graphql_mutation(mutation, current_user: current_user) }
-
- def mutation_response
- graphql_mutation_response(:create_board)
- end
-
context 'when the user does not have permission' do
it_behaves_like 'a mutation that returns a top-level access error'
diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
index 56b6dc682eb..2c6118779e6 100644
--- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
@@ -85,3 +85,14 @@ RSpec.shared_examples 'a Note mutation when there are rate limit validation erro
end
end
end
+
+RSpec.shared_examples 'a Note mutation with confidential notes' do
+ it_behaves_like 'a Note mutation that creates a Note'
+
+ it 'returns a Note with confidentiality enabled' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to have_key('note')
+ expect(mutation_response['note']['confidential']).to eq(true)
+ end
+end
diff --git a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
index efb2c466f70..3caf153c2fa 100644
--- a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
@@ -62,9 +62,9 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
expect(deprecable.deprecation_reason).to include 'This was renamed.'
end
- it 'supports named reasons: discouraged' do
- deprecable = subject(deprecated: { milestone: '1.10', reason: :discouraged })
+ it 'supports named reasons: alpha' do
+ deprecable = subject(deprecated: { milestone: '1.10', reason: :alpha })
- expect(deprecable.deprecation_reason).to include 'Use of this is not recommended.'
+ expect(deprecable.deprecation_reason).to include 'This feature is in Alpha'
end
end
diff --git a/spec/support/shared_examples/helpers/wiki_helpers_shared_examples.rb b/spec/support/shared_examples/helpers/wiki_helpers_shared_examples.rb
new file mode 100644
index 00000000000..c2c27fb65ca
--- /dev/null
+++ b/spec/support/shared_examples/helpers/wiki_helpers_shared_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'wiki endpoint helpers' do
+ let(:resource_path) { page.wiki.container.class.to_s.pluralize.downcase }
+ let(:url) { "/api/v4/#{resource_path}/#{page.wiki.container.id}/wikis/#{page.slug}?version=#{page.version.id}"}
+
+ it 'returns the full endpoint url' do
+ expect(helper.wiki_page_render_api_endpoint(page)).to end_with(url)
+ end
+
+ context 'when relative url is set' do
+ let(:relative_url) { "/gitlab#{url}" }
+
+ it 'returns the full endpoint url with the relative path' do
+ stub_config_setting(relative_url_root: '/gitlab')
+
+ expect(helper.wiki_page_render_api_endpoint(page)).to end_with(relative_url)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/incident_management/issuable_escalation_statuses/build_examples.rb b/spec/support/shared_examples/incident_management/issuable_escalation_statuses/build_examples.rb
new file mode 100644
index 00000000000..050fdc3fff7
--- /dev/null
+++ b/spec/support/shared_examples/incident_management/issuable_escalation_statuses/build_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'initializes new escalation status with expected attributes' do |attributes = {}|
+ let(:expected_attributes) { attributes }
+
+ specify do
+ expect { execute }.to change { incident.escalation_status }
+ .from(nil)
+ .to(instance_of(::IncidentManagement::IssuableEscalationStatus))
+
+ expect(incident.escalation_status).to have_attributes(
+ id: nil,
+ issue_id: incident.id,
+ policy_id: nil,
+ escalations_started_at: nil,
+ status_event: nil,
+ **expected_attributes
+ )
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/event_store_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/event_store_shared_examples.rb
new file mode 100644
index 00000000000..4fc15cacab4
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/event_store_shared_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'subscribes to event' do
+ include AfterNextHelpers
+
+ it 'consumes the published event', :sidekiq_inline do
+ expect_next(described_class)
+ .to receive(:handle_event)
+ .with(instance_of(event.class))
+ .and_call_original
+
+ ::Gitlab::EventStore.publish(event)
+ end
+end
+
+def consume_event(subscriber:, event:)
+ subscriber.new.perform(event.class.name, event.data)
+end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
index 4b956c2b566..b5d93aec1bf 100644
--- a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'a daily tracked issuable event' do
stub_application_setting(usage_ping_enabled: true)
end
- def count_unique(date_from:, date_to:)
+ def count_unique(date_from: 1.minute.ago, date_to: 1.minute.from_now)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
end
@@ -14,6 +14,7 @@ RSpec.shared_examples 'a daily tracked issuable event' do
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user2)).to be_truthy
+ expect(count_unique).to eq(2)
end
end
diff --git a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
index b4c438771ce..d816754f328 100644
--- a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
+++ b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
@@ -34,8 +34,16 @@ RSpec.shared_examples 'ZenTao menu with CE version' do
expect(subject.link).to eq zentao_integration.url
end
- it 'contains only open ZenTao item' do
- expect(subject.renderable_items.map(&:item_id)).to match_array [:open_zentao]
+ it 'renders external-link icon' do
+ expect(subject.sprite_icon).to eq 'external-link'
+ end
+
+ it 'renders ZenTao menu' do
+ expect(subject.title).to eq s_('ZentaoIntegration|ZenTao')
+ end
+
+ it 'does not contain items' do
+ expect(subject.renderable_items.count).to eq 0
end
end
end
diff --git a/spec/support/shared_examples/lib/wikis_api_examples.rb b/spec/support/shared_examples/lib/wikis_api_examples.rb
index f068a7676ad..c57ac328a60 100644
--- a/spec/support/shared_examples/lib/wikis_api_examples.rb
+++ b/spec/support/shared_examples/lib/wikis_api_examples.rb
@@ -80,6 +80,8 @@ RSpec.shared_examples_for 'wikis API returns wiki page' do
context 'when wiki page has versions' do
let(:new_content) { 'New content' }
+ let(:old_content) { page.content }
+ let(:old_version_id) { page.version.id }
before do
wiki.update_page(page.page, content: new_content, message: 'updated page')
@@ -96,10 +98,10 @@ RSpec.shared_examples_for 'wikis API returns wiki page' do
end
context 'when version param is set' do
- let(:params) { { version: page.version.id } }
+ let(:params) { { version: old_version_id } }
it 'retrieves the specific page version' do
- expect(json_response['content']).to eq(page.content)
+ expect(json_response['content']).to eq(old_content)
end
context 'when version param is not valid or inexistent' do
diff --git a/spec/support/shared_examples/models/application_setting_shared_examples.rb b/spec/support/shared_examples/models/application_setting_shared_examples.rb
index 38f5c7be393..74ec6474e80 100644
--- a/spec/support/shared_examples/models/application_setting_shared_examples.rb
+++ b/spec/support/shared_examples/models/application_setting_shared_examples.rb
@@ -239,7 +239,7 @@ RSpec.shared_examples 'application settings examples' do
describe '#allowed_key_types' do
it 'includes all key types by default' do
- expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES)
+ expect(setting.allowed_key_types).to contain_exactly(*Gitlab::SSHPublicKey.supported_types)
end
it 'excludes disabled key types' do
diff --git a/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb
new file mode 100644
index 00000000000..c3e9ff5c91a
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/bulk_users_by_email_load_shared_examples.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a BulkUsersByEmailLoad model' do
+ describe '#users_by_emails' do
+ let_it_be(:user1) { create(:user, emails: [create(:email, email: 'user1@example.com')]) }
+ let_it_be(:user2) { create(:user, emails: [create(:email, email: 'user2@example.com')]) }
+
+ subject(:model) { described_class.new(id: non_existing_record_id) }
+
+ context 'when nothing is loaded' do
+ let(:passed_emails) { [user1.emails.first.email, user2.email] }
+
+ it 'preforms the yielded query and supplies the data with only emails desired' do
+ expect(model.users_by_emails(passed_emails).keys).to contain_exactly(*passed_emails)
+ end
+ end
+
+ context 'when store is preloaded', :request_store do
+ let(:passed_emails) { [user1.emails.first.email, user2.email, user1.email] }
+ let(:resource_data) do
+ {
+ user1.emails.first.email => instance_double('User'),
+ user2.email => instance_double('User')
+ }
+ end
+
+ before do
+ Gitlab::SafeRequestStore["user_by_email_for_users:#{model.class.name}:#{model.id}"] = resource_data
+ end
+
+ it 'passes back loaded data and does not update the items that already exist' do
+ users_by_emails = model.users_by_emails(passed_emails)
+
+ expect(users_by_emails.keys).to contain_exactly(*passed_emails)
+ expect(users_by_emails).to include(resource_data.merge(user1.email => user1))
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb b/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb
index 6b208c0024d..e625ba785d2 100644
--- a/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/from_set_operator_shared_examples.rb
@@ -20,6 +20,12 @@ RSpec.shared_examples 'from set operator' do |sql_klass|
expect(query.to_sql).to match(/FROM \(\(SELECT.+\)\n#{operator_keyword}\n\(SELECT.+\)\) users/m)
end
+ it "returns empty set when passing empty array" do
+ query = model.public_send(operator_method, [])
+
+ expect(query.to_sql).to match(/WHERE \(1=0\)/m)
+ end
+
it 'supports the use of a custom alias for the sub query' do
query = model.public_send(operator_method,
[model.where(id: 1), model.where(id: 2)],
diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
index d6415e98289..da5c35c970a 100644
--- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
@@ -227,9 +227,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
end
context 'for confidential notes' do
- before_all do
- issue_note.update!(confidential: true)
- end
+ let_it_be(:issue_note) { create(:note_on_issue, project: project, note: "issue note", confidential: true) }
it 'falls back to note channel' do
expect(::Slack::Messenger).to execute_with_options(channel: ['random'])
diff --git a/spec/support/shared_examples/models/group_shared_examples.rb b/spec/support/shared_examples/models/group_shared_examples.rb
new file mode 100644
index 00000000000..9f3359ba4ab
--- /dev/null
+++ b/spec/support/shared_examples/models/group_shared_examples.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'checks self and root ancestor feature flag' do
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ subject { group.public_send(feature_flag_method) }
+
+ context 'when FF is enabled for the root group' do
+ before do
+ stub_feature_flags(feature_flag => root_group)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when FF is enabled for the group' do
+ before do
+ stub_feature_flags(feature_flag => group)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'when root_group is the actor' do
+ it 'is not enabled if the FF is enabled for a child' do
+ expect(root_group.public_send(feature_flag_method)).to be_falsey
+ end
+ end
+ end
+
+ context 'when FF is disabled globally' do
+ before do
+ stub_feature_flags(feature_flag => false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when FF is enabled globally' do
+ it { is_expected.to be_truthy }
+ end
+end
diff --git a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
deleted file mode 100644
index 13ffc1b7f87..00000000000
--- a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-# This shared example requires a `builder` and `user` variable
-RSpec.shared_examples 'issuable hook data' do |kind|
- let(:data) { builder.build(user: user) }
-
- include_examples 'project hook data' do
- let(:project) { builder.issuable.project }
- end
-
- include_examples 'deprecated repository hook data'
-
- context "with a #{kind}" do
- it 'contains issuable data' do
- expect(data[:object_kind]).to eq(kind)
- expect(data[:user]).to eq(user.hook_attrs)
- expect(data[:project]).to eq(builder.issuable.project.hook_attrs)
- expect(data[:object_attributes]).to eq(builder.issuable.hook_attrs)
- expect(data[:changes]).to eq({})
- expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage))
- end
-
- it 'does not contain certain keys' do
- expect(data).not_to have_key(:assignees)
- expect(data).not_to have_key(:assignee)
- end
-
- describe 'changes are given' do
- let(:changes) do
- {
- cached_markdown_version: %w[foo bar],
- description: ['A description', 'A cool description'],
- description_html: %w[foo bar],
- in_progress_merge_commit_sha: %w[foo bar],
- lock_version: %w[foo bar],
- merge_jid: %w[foo bar],
- title: ['A title', 'Hello World'],
- title_html: %w[foo bar]
- }
- end
-
- let(:data) { builder.build(user: user, changes: changes) }
-
- it 'populates the :changes hash' do
- expect(data[:changes]).to match(hash_including({
- title: { previous: 'A title', current: 'Hello World' },
- description: { previous: 'A description', current: 'A cool description' }
- }))
- end
-
- it 'does not contain certain keys' do
- expect(data[:changes]).not_to have_key('cached_markdown_version')
- expect(data[:changes]).not_to have_key('description_html')
- expect(data[:changes]).not_to have_key('lock_version')
- expect(data[:changes]).not_to have_key('title_html')
- expect(data[:changes]).not_to have_key('in_progress_merge_commit_sha')
- expect(data[:changes]).not_to have_key('merge_jid')
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/models/issuable_link_shared_examples.rb b/spec/support/shared_examples/models/issuable_link_shared_examples.rb
index ca98c2597a2..9892e66b582 100644
--- a/spec/support/shared_examples/models/issuable_link_shared_examples.rb
+++ b/spec/support/shared_examples/models/issuable_link_shared_examples.rb
@@ -55,6 +55,19 @@ RSpec.shared_examples 'issuable link' do
end
end
+ describe 'scopes' do
+ describe '.for_source_or_target' do
+ it 'returns only links where id is either source or target id' do
+ link1 = create(issuable_link_factory, source: issuable_link.source)
+ link2 = create(issuable_link_factory, target: issuable_link.source)
+ # unrelated link, should not be included in result list
+ create(issuable_link_factory) # rubocop: disable Rails/SaveBang
+
+ expect(described_class.for_source_or_target(issuable_link.source_id)).to match_array([issuable_link, link1, link2])
+ end
+ end
+ end
+
describe '.link_type' do
it { is_expected.to define_enum_for(:link_type).with_values(relates_to: 0, blocks: 1) }
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 17026f085bb..a329a6dca91 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -88,19 +88,55 @@ RSpec.shared_examples_for "member creation" do
expect(member).to be_persisted
end
- context 'when admin mode is enabled', :enable_admin_mode do
+ context 'when adding a project_bot' do
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+
+ before_all do
+ source.add_owner(user)
+ end
+
+ context 'when project_bot is already a member' do
+ before do
+ source.add_developer(project_bot)
+ end
+
+ it 'does not update the member' do
+ member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
+
+ expect(source.users.reload).to include(project_bot)
+ expect(member).to be_persisted
+ expect(member.access_level).to eq(Gitlab::Access::DEVELOPER)
+ expect(member.errors.full_messages).to include(/not authorized to update member/)
+ end
+ end
+
+ context 'when project_bot is not already a member' do
+ it 'adds the member' do
+ member = described_class.new(source, project_bot, :maintainer, current_user: user).execute
+
+ expect(source.users.reload).to include(project_bot)
+ expect(member).to be_persisted
+ end
+ end
+ end
+
+ context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do
it 'sets members.created_by to the given admin current_user' do
member = described_class.new(source, user, :maintainer, current_user: admin).execute
+ expect(member).to be_persisted
+ expect(source.users.reload).to include(user)
expect(member.created_by).to eq(admin)
end
end
context 'when admin mode is disabled' do
- it 'rejects setting members.created_by to the given admin current_user' do
+ it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do
member = described_class.new(source, user, :maintainer, current_user: admin).execute
- expect(member.created_by).to be_nil
+ expect(member).not_to be_persisted
+ expect(source.users.reload).not_to include(user)
+ expect(member.errors.full_messages).to include(/not authorized to create member/)
end
end
@@ -142,7 +178,7 @@ RSpec.shared_examples_for "member creation" do
end
context 'when called with an unknown user id' do
- it 'adds the user as a member' do
+ it 'does not add the user as a member' do
expect(source.users).not_to include(user)
described_class.new(source, non_existing_record_id, :maintainer).execute
@@ -410,6 +446,22 @@ RSpec.shared_examples_for "bulk member creation" do
end
end
+ it 'with the same user sent more than once by user and by email' do
+ members = described_class.add_users(source, [user1, user1.email], :maintainer)
+
+ expect(members.map(&:user)).to contain_exactly(user1)
+ expect(members).to all(be_a(member_type))
+ expect(members).to all(be_persisted)
+ end
+
+ it 'with the same user sent more than once by user id and by email' do
+ members = described_class.add_users(source, [user1.id, user1.email], :maintainer)
+
+ expect(members.map(&:user)).to contain_exactly(user1)
+ expect(members).to all(be_a(member_type))
+ expect(members).to all(be_persisted)
+ end
+
context 'when a member already exists' do
before do
source.add_user(user1, :developer)
diff --git a/spec/support/shared_examples/models/project_shared_examples.rb b/spec/support/shared_examples/models/project_shared_examples.rb
new file mode 100644
index 00000000000..475ac1da04b
--- /dev/null
+++ b/spec/support/shared_examples/models/project_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'returns true if project is inactive' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:storage_size, :last_activity_at, :expected_result) do
+ 1.megabyte | 1.month.ago | false
+ 1.megabyte | 3.years.ago | false
+ 8.megabytes | 1.month.ago | false
+ 8.megabytes | 3.years.ago | true
+ end
+
+ with_them do
+ before do
+ stub_application_setting(inactive_projects_min_size_mb: 5)
+ stub_application_setting(inactive_projects_send_warning_email_after_months: 24)
+
+ project.statistics.storage_size = storage_size
+ project.last_activity_at = last_activity_at
+ project.save!
+ end
+
+ it 'returns expected result' do
+ expect(project.inactive?).to eq(expected_result)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index b3f79d9fe6e..03e9dd65e33 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -11,6 +11,10 @@ RSpec.shared_examples 'wiki model' do
subject { wiki }
+ it 'VALID_USER_MARKUPS contains all valid markups' do
+ expect(described_class::VALID_USER_MARKUPS.keys).to match_array(%i(markdown rdoc asciidoc org))
+ end
+
it 'container class includes HasWiki' do
# NOTE: This is not enforced at runtime, since we also need to support Geo::DeletedProject
expect(wiki_container).to be_kind_of(HasWiki)
@@ -427,45 +431,131 @@ RSpec.shared_examples 'wiki model' do
end
describe '#update_page' do
- let(:page) { create(:wiki_page, wiki: subject, title: 'update-page') }
+ shared_examples 'update_page tests' do
+ with_them do
+ let!(:page) { create(:wiki_page, wiki: subject, title: original_title, format: original_format, content: 'original content') }
+
+ let(:message) { 'updated page' }
+ let(:updated_content) { 'updated content' }
+
+ def update_page
+ subject.update_page(
+ page.page,
+ content: updated_content,
+ title: updated_title,
+ format: updated_format,
+ message: message
+ )
+ end
+
+ specify :aggregate_failures do
+ expect(subject).to receive(:after_wiki_activity)
+ expect(update_page).to eq true
+
+ page = subject.find_page(updated_title.presence || original_title)
- def update_page
- subject.update_page(
- page.page,
- content: 'some other content',
- format: :markdown,
- message: 'updated page'
- )
+ expect(page.raw_content).to eq(updated_content)
+ expect(page.path).to eq(expected_path)
+ expect(page.version.message).to eq(message)
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ end
+ end
end
- it 'updates the content of the page' do
- update_page
- page = subject.find_page('update-page')
+ shared_context 'common examples' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do
+ 'test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md'
+ 'test page' | :markdown | 'test page' | :markdown | 'test-page.md'
+ 'test page' | :markdown | 'test page' | :asciidoc | 'test-page.asciidoc'
+
+ 'test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md'
+ 'test page' | :markdown | 'new dir/test page' | :markdown | 'new-dir/test-page.md'
- expect(page.raw_content).to eq('some other content')
+ 'test dir/test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md'
+ 'test dir/test page' | :markdown | 'test dir/test page' | :markdown | 'test-dir/test-page.md'
+ 'test dir/test page' | :markdown | 'test dir/test page' | :asciidoc | 'test-dir/test-page.asciidoc'
+
+ 'test dir/test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md'
+ 'test dir/test page' | :markdown | 'test page' | :markdown | 'test-page.md'
+
+ 'test page' | :markdown | nil | :markdown | 'test-page.md'
+ 'test.page' | :markdown | nil | :markdown | 'test.page.md'
+ end
end
- it 'sets the correct commit message' do
- update_page
- page = subject.find_page('update-page')
+ # There are two bugs in Gollum. THe first one is when the title and the format are updated
+ # at the same time https://gitlab.com/gitlab-org/gitlab/-/issues/243519.
+ # The second one is when the wiki page is within a dir and the `title` argument
+ # we pass to the update method is `nil`. Gollum will remove the dir and move the page.
+ #
+ # We can include this context into the former once it is fixed
+ # or when Gollum is removed since the Gitaly approach already fixes it.
+ shared_context 'extended examples' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do
+ 'test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc'
+ 'test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc'
+ 'test dir/test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc'
+ 'test dir/test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc'
+ 'test page' | :markdown | nil | :asciidoc | 'test-page.asciidoc'
+ 'test dir/test page' | :markdown | nil | :asciidoc | 'test-dir/test-page.asciidoc'
+ 'test dir/test page' | :markdown | nil | :markdown | 'test-dir/test-page.md'
+ 'test page' | :markdown | '' | :markdown | 'test-page.md'
+ 'test.page' | :markdown | '' | :markdown | 'test.page.md'
+ end
+ end
- expect(page.version.message).to eq('updated page')
+ it_behaves_like 'update_page tests' do
+ include_context 'common examples'
+ include_context 'extended examples'
end
- it 'sets the correct commit email' do
- update_page
+ context 'when format is invalid' do
+ let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
- expect(user.commit_email).not_to eq(user.email)
- expect(commit.author_email).to eq(user.commit_email)
- expect(commit.committer_email).to eq(user.commit_email)
+ it 'returns false and sets error message' do
+ expect(subject.update_page(page.page, content: 'new content', format: :foobar)).to eq false
+ expect(subject.error_message).to match(/Invalid format selected/)
+ end
end
- it 'runs after_wiki_activity callbacks' do
- page
+ context 'when format is not allowed' do
+ let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
- expect(subject).to receive(:after_wiki_activity)
+ it 'returns false and sets error message' do
+ expect(subject.update_page(page.page, content: 'new content', format: :creole)).to eq false
+ expect(subject.error_message).to match(/Invalid format selected/)
+ end
+ end
+
+ context 'when page path does not have a default extension' do
+ let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
+
+ context 'when format is not different' do
+ it 'does not change the default extension' do
+ path = 'test-page.markdown'
+ page.page.instance_variable_set(:@path, path)
- update_page
+ expect(subject.repository).to receive(:update_file).with(user, path, anything, anything)
+
+ subject.update_page(page.page, content: 'new content', format: :markdown)
+ end
+ end
+ end
+
+ context 'when feature flag :gitaly_replace_wiki_update_page is disabled' do
+ before do
+ stub_feature_flags(gitaly_replace_wiki_update_page: false)
+ end
+
+ it_behaves_like 'update_page tests' do
+ include_context 'common examples'
+ end
end
end
diff --git a/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
index 58822f4309b..991d6289373 100644
--- a/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
+++ b/spec/support/shared_examples/policies/wiki_policies_shared_examples.rb
@@ -107,10 +107,4 @@ RSpec.shared_examples 'model with wiki policies' do
expect_disallowed(*disallowed_permissions)
end
end
-
- # TODO: Remove this helper once we implement group features
- # https://gitlab.com/gitlab-org/gitlab/-/issues/208412
- def set_access_level(access_level)
- raise NotImplementedError
- end
end
diff --git a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
index 9f4fdcf7ba1..dc2c4f890b1 100644
--- a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
@@ -163,11 +163,11 @@ RSpec.shared_examples 'rejects Composer access with unknown project id' do
let(:project) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'process Composer api request', :anonymous, :not_found
+ it_behaves_like 'process Composer api request', :anonymous, :unauthorized
end
context 'as authenticated user' do
- subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
+ subject { get api(url), params: params, headers: basic_auth_header(user.username, personal_access_token.token) }
it_behaves_like 'process Composer api request', :anonymous, :not_found
end
diff --git a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
index e1e75be2494..c1eccafa987 100644
--- a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
@@ -116,3 +116,93 @@ RSpec.shared_examples 'not hitting graphql network errors with the container reg
expect_graphql_errors_to_be_empty
end
end
+
+RSpec.shared_examples 'reconciling migration_state' do
+ shared_examples 'enforcing states coherence to' do |expected_migration_state|
+ it 'leaves the repository in the expected migration_state' do
+ expect(repository.gitlab_api_client).not_to receive(:pre_import_repository)
+ expect(repository.gitlab_api_client).not_to receive(:import_repository)
+
+ subject
+
+ expect(repository.reload.migration_state).to eq(expected_migration_state)
+ end
+ end
+
+ shared_examples 'retrying the pre_import' do
+ it 'retries the pre_import' do
+ expect(repository).to receive(:migration_pre_import).and_return(:ok)
+
+ expect { subject }.to change { repository.reload.migration_state }.to('pre_importing')
+ end
+ end
+
+ shared_examples 'retrying the import' do
+ it 'retries the import' do
+ expect(repository).to receive(:migration_import).and_return(:ok)
+
+ expect { subject }.to change { repository.reload.migration_state }.to('importing')
+ end
+ end
+
+ context 'native response' do
+ let(:status) { 'native' }
+
+ it 'finishes the import' do
+ expect { subject }
+ .to change { repository.reload.migration_state }.to('import_done')
+ .and change { repository.reload.migration_skipped_reason }.to('native_import')
+ end
+ end
+
+ context 'import_in_progress response' do
+ let(:status) { 'import_in_progress' }
+
+ it_behaves_like 'enforcing states coherence to', 'importing'
+ end
+
+ context 'import_complete response' do
+ let(:status) { 'import_complete' }
+
+ it 'finishes the import' do
+ expect { subject }.to change { repository.reload.migration_state }.to('import_done')
+ end
+ end
+
+ context 'import_failed response' do
+ let(:status) { 'import_failed' }
+
+ it_behaves_like 'retrying the import'
+ end
+
+ context 'pre_import_in_progress response' do
+ let(:status) { 'pre_import_in_progress' }
+
+ it_behaves_like 'enforcing states coherence to', 'pre_importing'
+ end
+
+ context 'pre_import_complete response' do
+ let(:status) { 'pre_import_complete' }
+
+ it 'finishes the pre_import and starts the import' do
+ expect(repository).to receive(:finish_pre_import).and_call_original
+ expect(repository).to receive(:migration_import).and_return(:ok)
+
+ expect { subject }.to change { repository.reload.migration_state }.to('importing')
+ end
+ end
+
+ context 'pre_import_failed response' do
+ let(:status) { 'pre_import_failed' }
+
+ it_behaves_like 'retrying the pre_import'
+ end
+
+ %w[pre_import_canceled import_canceled].each do |canceled_status|
+ context "#{canceled_status} response" do
+ let(:status) { canceled_status }
+
+ it_behaves_like 'enforcing states coherence to', 'import_skipped'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
index 01ed6c26576..da9d254039b 100644
--- a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb
@@ -54,11 +54,13 @@ RSpec.shared_examples 'group and project boards query' do
end
context 'when using default sorting' do
+ # rubocop:disable RSpec/VariableName
let!(:board_B) { create(:board, resource_parent: board_parent, name: 'B') }
let!(:board_C) { create(:board, resource_parent: board_parent, name: 'C') }
let!(:board_a) { create(:board, resource_parent: board_parent, name: 'a') }
let!(:board_A) { create(:board, resource_parent: board_parent, name: 'A') }
let(:boards) { [board_a, board_A, board_B, board_C] }
+ # rubocop:enable RSpec/VariableName
context 'when ascending' do
it_behaves_like 'sorted paginated query' do
diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
index c5e5803c0a7..673d7741017 100644
--- a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
@@ -28,34 +28,4 @@ RSpec.shared_examples 'issuable participants endpoint' do
expect(response).to have_gitlab_http_status(:not_found)
end
-
- context 'with a confidential note' do
- let!(:note) do
- create(
- :note,
- :confidential,
- project: project,
- noteable: entity,
- author: create(:user)
- )
- end
-
- it 'returns a full list of participants' do
- get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user)
-
- expect(response).to have_gitlab_http_status(:ok)
- participant_ids = json_response.map { |el| el['id'] }
- expect(participant_ids).to match_array([entity.author_id, note.author_id])
- end
-
- context 'when user cannot see a confidential note' do
- it 'returns a limited list of participants' do
- get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", create(:user))
-
- expect(response).to have_gitlab_http_status(:ok)
- participant_ids = json_response.map { |el| el['id'] }
- expect(participant_ids).to match_array([entity.author_id])
- end
- end
- end
end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index 2a157f6e855..e7e30665b08 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -142,15 +142,6 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect(json_response['author']['username']).to eq(user.username)
end
- it "creates a confidential note if confidential is set to true" do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: { body: 'hi!', confidential: true }
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['body']).to eq('hi!')
- expect(json_response['confidential']).to be_truthy
- expect(json_response['author']['username']).to eq(user.username)
- end
-
it "returns a 400 bad request error if body not given" do
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
@@ -306,52 +297,31 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do
- let(:params) { { body: 'Hello!', confidential: false } }
+ let(:params) { { body: 'Hello!' } }
subject do
put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user), params: params
end
- context 'when eveything is ok' do
- before do
- note.update!(confidential: true)
- end
+ context 'when only body param is present' do
+ let(:params) { { body: 'Hello!' } }
- context 'with multiple params present' do
- before do
- subject
- end
-
- it 'returns modified note' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['body']).to eq('Hello!')
- expect(json_response['confidential']).to be_falsey
- end
-
- it 'updates the note' do
- expect(note.reload.note).to eq('Hello!')
- expect(note.confidential).to be_falsey
- end
- end
-
- context 'when only body param is present' do
- let(:params) { { body: 'Hello!' } }
-
- it 'updates only the note text' do
- expect { subject }.not_to change { note.reload.confidential }
+ it 'updates the note text' do
+ subject
- expect(note.note).to eq('Hello!')
- end
+ expect(note.reload.note).to eq('Hello!')
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['body']).to eq('Hello!')
end
+ end
- context 'when only confidential param is present' do
- let(:params) { { confidential: false } }
+ context 'when confidential param is present' do
+ let(:params) { { confidential: true } }
- it 'updates only the note text' do
- expect { subject }.not_to change { note.reload.note }
+ it 'does not allow to change confidentiality' do
+ expect { subject }.not_to change { note.reload.note }
- expect(note.confidential).to be_falsey
- end
+ expect(response).to have_gitlab_http_status(:bad_request)
end
end
@@ -393,3 +363,24 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
end
end
+
+RSpec.shared_examples 'noteable API with confidential notes' do |parent_type, noteable_type, id_name|
+ it_behaves_like 'noteable API', parent_type, noteable_type, id_name
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
+ let(:params) { { body: 'hi!' } }
+
+ subject do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ end
+
+ it "creates a confidential note if confidential is set to true" do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params.merge(confidential: true)
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['confidential']).to be_truthy
+ expect(json_response['author']['username']).to eq(user.username)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
index 87a33060435..fcd52cdf7fa 100644
--- a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
@@ -1,8 +1,5 @@
# frozen_string_literal: true
RSpec.shared_examples 'avoid N+1 on environments serialization' do |ee: false|
- # Investigating in https://gitlab.com/gitlab-org/gitlab/-/issues/353209
- let(:query_threshold) { 1 + (ee ? 4 : 0) }
-
it 'avoids N+1 database queries with grouping', :request_store do
create_environment_with_associations(project)
@@ -11,9 +8,11 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do |ee: false|
create_environment_with_associations(project)
create_environment_with_associations(project)
- expect { serialize(grouping: true) }
- .not_to exceed_query_limit(control.count)
- .with_threshold(query_threshold)
+ # Fix N+1 queries introduced by multi stop_actions for environment.
+ # Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/358780
+ relax_count = 14
+
+ expect { serialize(grouping: true) }.not_to exceed_query_limit(control.count + relax_count)
end
it 'avoids N+1 database queries without grouping', :request_store do
@@ -24,9 +23,11 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do |ee: false|
create_environment_with_associations(project)
create_environment_with_associations(project)
- expect { serialize(grouping: false) }
- .not_to exceed_query_limit(control.count)
- .with_threshold(query_threshold)
+ # Fix N+1 queries introduced by multi stop_actions for environment.
+ # Tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/358780
+ relax_count = 14
+
+ expect { serialize(grouping: false) }.not_to exceed_query_limit(control.count + relax_count)
end
it 'does not preload for environments that does not exist in the page', :request_store do
diff --git a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
index 0e2bddc19ab..fd832d4484d 100644
--- a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
@@ -13,10 +13,12 @@ RSpec.shared_examples 'boards list service' do
end
RSpec.shared_examples 'multiple boards list service' do
+ # rubocop:disable RSpec/VariableName
let(:service) { described_class.new(parent, double) }
let!(:board_B) { create(:board, resource_parent: parent, name: 'B-board') }
let!(:board_c) { create(:board, resource_parent: parent, name: 'c-board') }
let!(:board_a) { create(:board, resource_parent: parent, name: 'a-board') }
+ # rubocop:enable RSpec/VariableName
describe '#execute' do
it 'returns all issue boards' do
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index a780952d51b..7677e5d8cb2 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -154,6 +154,30 @@ RSpec.shared_examples 'logs an auth warning' do |requested_actions|
end
end
+RSpec.shared_examples 'allowed to delete container repository images' do
+ let(:authentication_abilities) do
+ [:admin_container_image]
+ end
+
+ it_behaves_like 'a valid token'
+
+ context 'allow to delete images' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:*"] }
+ end
+
+ it_behaves_like 'a deletable'
+ end
+
+ context 'allow to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'a deletable since registry 2.7'
+ end
+end
+
RSpec.shared_examples 'a container registry auth service' do
include_context 'container registry auth service context'
@@ -204,6 +228,46 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'not a container repository factory'
end
+ describe '.pull_nested_repositories_access_token' do
+ let_it_be(:project) { create(:project) }
+
+ let(:token) { described_class.pull_nested_repositories_access_token(project.full_path) }
+ let(:access) do
+ [
+ {
+ 'type' => 'repository',
+ 'name' => project.full_path,
+ 'actions' => ['pull']
+ },
+ {
+ 'type' => 'repository',
+ 'name' => "#{project.full_path}/*",
+ 'actions' => ['pull']
+ }
+ ]
+ end
+
+ subject { { token: token } }
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+
+ it_behaves_like 'a valid token'
+ it_behaves_like 'not a container repository factory'
+
+ context 'with path ending with a slash' do
+ let(:token) { described_class.pull_nested_repositories_access_token("#{project.full_path}/") }
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+
+ it_behaves_like 'a valid token'
+ it_behaves_like 'not a container repository factory'
+ end
+ end
+
context 'user authorization' do
let_it_be(:current_user) { create(:user) }
@@ -504,38 +568,14 @@ RSpec.shared_examples 'a container registry auth service' do
end
context 'delete authorized as maintainer' do
- let_it_be(:current_project) { create(:project) }
+ let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
- let(:authentication_abilities) do
- [:admin_container_image]
- end
-
before_all do
- current_project.add_maintainer(current_user)
- end
-
- it_behaves_like 'a valid token'
-
- context 'allow to delete images' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:*"] }
- end
-
- it_behaves_like 'a deletable' do
- let(:project) { current_project }
- end
+ project.add_maintainer(current_user)
end
- context 'allow to delete images since registry 2.7' do
- let(:current_params) do
- { scopes: ["repository:#{current_project.full_path}:delete"] }
- end
-
- it_behaves_like 'a deletable since registry 2.7' do
- let(:project) { current_project }
- end
- end
+ it_behaves_like 'allowed to delete container repository images'
end
context 'build authorized as user' do
diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
index 6146aae6b9b..9610cdd18a3 100644
--- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
@@ -70,8 +70,10 @@ shared_examples 'issuable link creation' do
expect(issuable_link_class.find_by!(target: issuable3)).to have_attributes(source: issuable, link_type: 'relates_to')
end
- it 'returns success status' do
- is_expected.to eq(status: :success)
+ it 'returns success status and created links', :aggregate_failures do
+ expect(subject.keys).to match_array([:status, :created_references])
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:created_references].map(&:target_id)).to match_array([issuable2.id, issuable3.id])
end
it 'creates notes' do
diff --git a/spec/support/shared_examples/views/milestone_shared_examples.rb b/spec/support/shared_examples/views/milestone_shared_examples.rb
new file mode 100644
index 00000000000..b6f4d0db0e9
--- /dev/null
+++ b/spec/support/shared_examples/views/milestone_shared_examples.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'milestone empty states' do
+ include Devise::Test::ControllerHelpers
+
+ let_it_be(:user) { build(:user) }
+ let(:empty_state) { 'Use milestones to track issues and merge requests over a fixed period of time' }
+
+ before do
+ assign(:projects, [])
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ context 'with no milestones' do
+ before do
+ assign(:milestones, [])
+ assign(:milestone_states, { opened: 0, closed: 0, all: 0 })
+ render
+ end
+
+ it 'shows empty state' do
+ expect(rendered).to have_content(empty_state)
+ end
+
+ it 'does not show tabs or searchbar' do
+ expect(rendered).not_to have_link('Open')
+ expect(rendered).not_to have_link('Closed')
+ expect(rendered).not_to have_link('All')
+ end
+ end
+
+ context 'with no open milestones' do
+ before do
+ allow(view).to receive(:milestone_path).and_return("/milestones/1")
+ assign(:milestones, [])
+ assign(:milestone_states, { opened: 0, closed: 1, all: 1 })
+ end
+
+ it 'shows tabs and searchbar', :aggregate_failures do
+ render
+
+ expect(rendered).not_to have_content(empty_state)
+ expect(rendered).to have_link('Open')
+ expect(rendered).to have_link('Closed')
+ expect(rendered).to have_link('All')
+ end
+
+ it 'shows empty state' do
+ render
+
+ expect(rendered).to have_content('There are no open milestones')
+ end
+ end
+
+ context 'with no closed milestones' do
+ before do
+ allow(view).to receive(:milestone_path).and_return("/milestones/1")
+ allow(view).to receive(:params).and_return(state: 'closed')
+ assign(:milestones, [])
+ assign(:milestone_states, { opened: 1, closed: 0, all: 1 })
+ end
+
+ it 'shows tabs and searchbar', :aggregate_failures do
+ render
+
+ expect(rendered).not_to have_content(empty_state)
+ expect(rendered).to have_link('Open')
+ expect(rendered).to have_link('Closed')
+ expect(rendered).to have_link('All')
+ end
+
+ it 'shows empty state on closed milestones' do
+ render
+
+ expect(rendered).to have_content('There are no closed milestones')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
index d202c4e00f0..26731f34ed6 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_database|
+RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_database, feature_flag:|
include ExclusiveLeaseHelpers
describe 'defining the job attributes' do
@@ -39,6 +39,16 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
+ describe '.enabled?' do
+ it 'does not raise an error' do
+ expect { described_class.enabled? }.not_to raise_error
+ end
+
+ it 'returns true' do
+ expect(described_class.enabled?).to be_truthy
+ end
+ end
+
describe '#perform' do
subject(:worker) { described_class.new }
@@ -76,7 +86,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
context 'when the feature flag is disabled' do
before do
- stub_feature_flags(execute_batched_migrations_on_schedule: false)
+ stub_feature_flags(feature_flag => false)
end
it 'does nothing' do
@@ -89,7 +99,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
context 'when the feature flag is enabled' do
before do
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
+ stub_feature_flags(feature_flag => true)
allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration).and_return(nil)
end
diff --git a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
index 202606c6aa6..4751d91efde 100644
--- a/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
+++ b/spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb
@@ -230,76 +230,6 @@ RSpec.shared_examples 'can collect git garbage' do |update_statistics: true|
stub_feature_flags(optimized_housekeeping: false)
end
- it 'incremental repack adds a new packfile' do
- create_objects(resource)
- before_packs = packs(resource)
-
- expect(before_packs.count).to be >= 1
-
- subject.perform(resource.id, 'incremental_repack', lease_key, lease_uuid)
- after_packs = packs(resource)
-
- # Exactly one new pack should have been created
- expect(after_packs.count).to eq(before_packs.count + 1)
-
- # Previously existing packs are still around
- expect(before_packs & after_packs).to eq(before_packs)
- end
-
- it 'full repack consolidates into 1 packfile' do
- create_objects(resource)
- subject.perform(resource.id, 'incremental_repack', lease_key, lease_uuid)
- before_packs = packs(resource)
-
- expect(before_packs.count).to be >= 2
-
- subject.perform(resource.id, 'full_repack', lease_key, lease_uuid)
- after_packs = packs(resource)
-
- expect(after_packs.count).to eq(1)
-
- # Previously existing packs should be gone now
- expect(after_packs - before_packs).to eq(after_packs)
-
- expect(File.exist?(bitmap_path(after_packs.first))).to eq(bitmaps_enabled)
- end
-
- it 'gc consolidates into 1 packfile and updates packed-refs' do
- create_objects(resource)
- before_packs = packs(resource)
- before_packed_refs = packed_refs(resource)
-
- expect(before_packs.count).to be >= 1
-
- # It's quite difficult to use `expect_next_instance_of` in this place
- # because the RepositoryService is instantiated several times to do
- # some repository calls like `exists?`, `create_repository`, ... .
- # Therefore, since we're instantiating the object several times,
- # RSpec has troubles figuring out which instance is the next and which
- # one we want to mock.
- # Besides, at this point, we actually want to perform the call to Gitaly,
- # otherwise we would just use `instance_double` like in other parts of the
- # spec file.
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService) # rubocop:disable RSpec/AnyInstanceOf
- .to receive(:garbage_collect)
- .with(bitmaps_enabled, prune: false)
- .and_call_original
-
- subject.perform(resource.id, 'gc', lease_key, lease_uuid)
- after_packed_refs = packed_refs(resource)
- after_packs = packs(resource)
-
- expect(after_packs.count).to eq(1)
-
- # Previously existing packs should be gone now
- expect(after_packs - before_packs).to eq(after_packs)
-
- # The packed-refs file should have been updated during 'git gc'
- expect(before_packed_refs).not_to eq(after_packed_refs)
-
- expect(File.exist?(bitmap_path(after_packs.first))).to eq(bitmaps_enabled)
- end
-
it 'cleans up repository after finishing' do
expect(resource).to receive(:cleanup).and_call_original