diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-19 17:16:28 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-19 17:16:28 +0300 |
commit | e4384360a16dd9a19d4d2d25d0ef1f2b862ed2a6 (patch) | |
tree | 2fcdfa7dcdb9db8f5208b2562f4b4e803d671243 /spec/support/shared_examples | |
parent | ffda4e7bcac36987f936b4ba515995a6698698f0 (diff) |
Add latest changes from gitlab-org/gitlab@16-2-stable-eev16.2.0-rc42
Diffstat (limited to 'spec/support/shared_examples')
63 files changed, 991 insertions, 537 deletions
diff --git a/spec/support/shared_examples/ci/stage_shared_examples.rb b/spec/support/shared_examples/ci/stage_shared_examples.rb index a2849e00d27..cdb1058e584 100644 --- a/spec/support/shared_examples/ci/stage_shared_examples.rb +++ b/spec/support/shared_examples/ci/stage_shared_examples.rb @@ -21,7 +21,7 @@ RSpec.shared_examples 'manual playable stage' do |stage_type| context 'when is skipped' do let(:status) { 'skipped' } - it { is_expected.to be_truthy } + it { is_expected.to be_falsy } end end end diff --git a/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb b/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb index c8eaef764af..e61c884cd2b 100644 --- a/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb +++ b/spec/support/shared_examples/config/metrics/every_metric_definition_shared_examples.rb @@ -113,7 +113,8 @@ RSpec.shared_examples 'every metric definition' do Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric, Gitlab::Usage::Metrics::Instrumentations::RedisMetric, Gitlab::Usage::Metrics::Instrumentations::RedisHLLMetric, - Gitlab::Usage::Metrics::Instrumentations::NumbersMetric + Gitlab::Usage::Metrics::Instrumentations::NumbersMetric, + Gitlab::Usage::Metrics::Instrumentations::PrometheusMetric ] end diff --git a/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb new file mode 100644 index 00000000000..e2a4fb31361 --- /dev/null +++ b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Requires a context containing: +# - subject +# - action +# - user +# Optionally, the context can contain: +# - project +# - namespace + +RSpec.shared_examples 'internal event tracking' do + let(:fake_tracker) { instance_spy(Gitlab::Tracking::Destinations::Snowplow) } + let(:namespace) { nil } + let(:proejct) { nil } + + before do + allow(Gitlab::Tracking).to receive(:tracker).and_return(fake_tracker) + + allow(Gitlab::Tracking::StandardContext).to receive(:new).and_call_original + allow(Gitlab::Tracking::ServicePingContext).to receive(:new).and_call_original + end + + it 'logs to Snowplow', :aggregate_failures do + subject + + expect(Gitlab::Tracking::StandardContext) + .to have_received(:new) + .with( + project_id: project&.id, + user_id: user.id, + namespace_id: namespace&.id, + plan_name: namespace&.actual_plan_name + ) + + expect(Gitlab::Tracking::ServicePingContext) + .to have_received(:new) + .with(data_source: :redis_hll, event: action) + + expect(fake_tracker).to have_received(:event) + .with( + 'InternalEventTracking', + action, + context: [ + an_instance_of(SnowplowTracker::SelfDescribingJson), + an_instance_of(SnowplowTracker::SelfDescribingJson) + ] + ) + .exactly(:once) + end +end diff --git a/spec/support/shared_examples/controllers/metrics_dashboard_shared_examples.rb b/spec/support/shared_examples/controllers/metrics_dashboard_shared_examples.rb deleted file mode 100644 index 5b63ef10c85..00000000000 --- a/spec/support/shared_examples/controllers/metrics_dashboard_shared_examples.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples_for 'GET #metrics_dashboard correctly formatted response' do - it 'returns a json object with the correct keys' do - get :metrics_dashboard, params: metrics_dashboard_req_params, format: :json - - # Exclude `all_dashboards` to handle separately, at spec/controllers/projects/environments_controller_spec.rb:565 - # because `all_dashboards` key is not part of expected shared behavior - found_keys = json_response.keys - ['all_dashboards'] - - expect(response).to have_gitlab_http_status(status_code) - expect(found_keys).to contain_exactly(*expected_keys) - end -end - -RSpec.shared_examples_for 'GET #metrics_dashboard for dashboard' do |dashboard_name| - let(:expected_keys) { %w(dashboard status metrics_data) } - let(:status_code) { :ok } - - before do - stub_feature_flags(remove_monitor_metrics: false) - end - - it_behaves_like 'GET #metrics_dashboard correctly formatted response' - - it 'returns correct dashboard' do - get :metrics_dashboard, params: metrics_dashboard_req_params, format: :json - - expect(json_response['dashboard']['dashboard']).to eq(dashboard_name) - end - - context 'when metrics dashboard feature is unavailable' do - before do - stub_feature_flags(remove_monitor_metrics: true) - end - - it 'returns 404 not found' do - get :metrics_dashboard, params: metrics_dashboard_req_params, format: :json - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.body).to be_empty - end - end -end diff --git a/spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb b/spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb index 9cf35325202..ba3b08751da 100644 --- a/spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb +++ b/spec/support/shared_examples/controllers/repository_lfs_file_load_shared_examples.rb @@ -75,7 +75,7 @@ RSpec.shared_examples 'a controller that can serve LFS files' do |options = {}| file_uri = URI.parse(response.location) params = CGI.parse(file_uri.query) - expect(params["response-content-disposition"].first).to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename})) + expect(params["response-content-disposition"].first).to eq(%(attachment; filename="#{filename}"; filename*=UTF-8''#{filename})) end end end diff --git a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb index ba00e3e0610..3d3b619451d 100644 --- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb +++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb @@ -6,7 +6,7 @@ # - category # - action # - namespace -# Optionaly, the context can contain: +# Optionally, the context can contain: # - project # - property # - user diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 0792ac14e47..772e03950da 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -94,14 +94,6 @@ RSpec.shared_examples 'handle uploads' do expect(response).to have_gitlab_http_status(:not_found) end - - it 'is a working exploit without the validation' do - allow_any_instance_of(FileUploader).to receive(:secret) { secret } - - show_upload - - expect(response).to have_gitlab_http_status(:ok) - end end context 'when accessing a specific upload via different model' do diff --git a/spec/support/shared_examples/features/cascading_settings_shared_examples.rb b/spec/support/shared_examples/features/cascading_settings_shared_examples.rb index cb80751ff49..2bda352c11f 100644 --- a/spec/support/shared_examples/features/cascading_settings_shared_examples.rb +++ b/spec/support/shared_examples/features/cascading_settings_shared_examples.rb @@ -24,14 +24,6 @@ RSpec.shared_examples 'a cascading setting' do include_examples 'subgroup settings are disabled' - context 'when use_traversal_ids_for_ancestors is disabled' do - before do - stub_feature_flags(use_traversal_ids_for_ancestors: false) - end - - include_examples 'subgroup settings are disabled' - end - it 'does not show enforcement checkbox in subgroups' do visit subgroup_path 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 f70288168d7..254bc3c83ac 100644 --- a/spec/support/shared_examples/features/content_editor_shared_examples.rb +++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb @@ -506,6 +506,8 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi switch_to_content_editor type_in_content_editor :enter + + stub_feature_flags(disable_all_mention: false) end if params[:with_expanded_references] @@ -545,12 +547,32 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi expect(page).to have_text('@abc123') end + context 'when `disable_all_mention` is enabled' do + before do + stub_feature_flags(disable_all_mention: true) + end + + it 'shows suggestions for members with descriptions' do + type_in_content_editor '@a' + + expect(find(suggestions_dropdown)).to have_text('abc123') + expect(find(suggestions_dropdown)).not_to have_text('All Group Members') + + type_in_content_editor 'bc' + + send_keys [:arrow_down, :enter] + + expect(page).not_to have_css(suggestions_dropdown) + expect(page).to have_text('@abc123') + end + end + it 'shows suggestions for merge requests' do type_in_content_editor '!' expect(find(suggestions_dropdown)).to have_text('My Cool Merge Request') - send_keys :enter + send_keys [:arrow_down, :enter] expect(page).not_to have_css(suggestions_dropdown) expect(page).to have_text('!1') @@ -561,7 +583,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi expect(find(suggestions_dropdown)).to have_text('My Cool Linked Issue') - send_keys :enter + send_keys [:arrow_down, :enter] expect(page).not_to have_css(suggestions_dropdown) expect(page).to have_text('#1') @@ -572,7 +594,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi expect(find(suggestions_dropdown)).to have_text('My Cool Milestone') - send_keys :enter + send_keys [:arrow_down, :enter] expect(page).not_to have_css(suggestions_dropdown) expect(page).to have_text('%My Cool Milestone') @@ -584,7 +606,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi expect(find(suggestions_dropdown)).to have_text('🙂 slight_smile') expect(find(suggestions_dropdown)).to have_text('😸 smile_cat') - send_keys :enter + send_keys [:arrow_down, :enter] expect(page).not_to have_css(suggestions_dropdown) @@ -614,7 +636,7 @@ RSpec.shared_examples 'edits content using the content editor' do |params = { wi end def dropdown_scroll_top - evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-dropdown-inner').scrollTop") + evaluate_script("document.querySelector('#{suggestions_dropdown}').scrollTop") end end end diff --git a/spec/support/shared_examples/features/discussion_comments_shared_example.rb b/spec/support/shared_examples/features/discussion_comments_shared_example.rb index d6f1efc09fc..430a8ac39d7 100644 --- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb +++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb @@ -3,8 +3,8 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name| let(:form_selector) { '.js-main-target-form' } let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" } - let(:toggle_selector) { "#{dropdown_selector} .gl-dropdown-toggle" } - let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" } + let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" } + let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" } let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" } let(:close_selector) { "#{form_selector} .btn-comment-and-close" } let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' } @@ -63,33 +63,6 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name expect(page).not_to have_selector menu_selector end - it 'clicking the ul padding or divider should not change the text' do - execute_script("document.querySelector('#{menu_selector}').click()") - - # on issues page, the menu closes when clicking anywhere, on other pages it will - # remain open if clicking divider or menu padding, but should not change button action - # - # if dropdown menu is not toggled (and also not present), - # it's "issue-type" dropdown - if first(menu_selector, minimum: 0).nil? - expect(find(dropdown_selector)).to have_content 'Comment' - - find(toggle_selector).click - execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()") - else - execute_script("document.querySelector('#{menu_selector}').click()") - - expect(page).to have_selector menu_selector - expect(find(dropdown_selector)).to have_content 'Comment' - - execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()") - - expect(page).to have_selector menu_selector - end - - expect(find(dropdown_selector)).to have_content 'Comment' - end - describe 'when selecting "Start thread"' do before do find("#{menu_selector} li", match: :first) @@ -177,21 +150,27 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name end RSpec.shared_examples 'thread comments for issue, epic and merge request' do |resource_name| + include ContentEditorHelpers + let(:form_selector) { '.js-main-target-form' } - let(:dropdown_selector) { "#{form_selector} [data-testid='comment-button']" } - let(:submit_button_selector) { "#{dropdown_selector} .split-content-button" } - let(:toggle_selector) { "#{dropdown_selector} .dropdown-toggle-split" } - let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" } + let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" } + let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" } + let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" } + let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" } let(:close_selector) { "#{form_selector} .btn-comment-and-close" } let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' } let(:comment) { 'My comment' } + before do + close_rich_text_promo_popover_if_present + end + it 'clicking "Comment" will post a comment' do expect(page).to have_selector toggle_selector find("#{form_selector} .note-textarea").send_keys(comment) - find(submit_button_selector).click + find(submit_selector).click wait_for_all_requests @@ -260,7 +239,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re describe 'creating a thread' do before do - find(submit_button_selector).click + find(submit_selector).click wait_for_requests find(comments_selector, match: :first) @@ -284,7 +263,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re expect(new_comment).to have_css('.discussion-with-resolve-btn') end - if resource_name =~ /(issue|merge request)/ + if /(issue|merge request)/.match?(resource_name) it 'can be replied to' do submit_reply('some text') @@ -366,14 +345,14 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re end it 'updates the submit button text and closes the dropdown' do - button = find(submit_button_selector) + button = find(submit_selector) expect(button).to have_content 'Comment' expect(page).not_to have_selector menu_selector end - if resource_name =~ /(issue|merge request)/ + if /(issue|merge request)/.match?(resource_name) it 'updates the close button text' do expect(find(close_selector)).to have_content "Comment & close #{resource_name}" end @@ -402,7 +381,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re end end - if resource_name =~ /(issue|merge request)/ + if /(issue|merge request)/.match?(resource_name) describe "on a closed #{resource_name}" do before do find("#{form_selector} .js-note-target-close").click diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb index 14e53dc8655..f802404518b 100644 --- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb @@ -125,7 +125,11 @@ RSpec.shared_examples 'an editable merge request' do it 'allows to unselect "Remove source branch"', :js do expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy - visit edit_project_merge_request_path(target_project, merge_request) + begin + visit edit_project_merge_request_path(target_project, merge_request) + rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError + end + uncheck 'Delete source branch when merge request is accepted' click_button 'Save changes' diff --git a/spec/support/shared_examples/features/inviting_members_shared_examples.rb b/spec/support/shared_examples/features/inviting_members_shared_examples.rb index 2eca2a72997..178f85cb85b 100644 --- a/spec/support/shared_examples/features/inviting_members_shared_examples.rb +++ b/spec/support/shared_examples/features/inviting_members_shared_examples.rb @@ -159,6 +159,40 @@ RSpec.shared_examples 'inviting members' do |snowplow_invite_label| end end + context 'when a user already exists, and private email is used' do + it 'fails with an error', :js do + visit subentity_members_page_path + + invite_member(user2.email, role: role) + + invite_modal = page.find(invite_modal_selector) + expect(invite_modal).to have_content "#{user2.email}: 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 + + it 'does not allow inviting of an email that has spaces', :js do + visit subentity_members_page_path + + click_on _('Invite members') + + page.within invite_modal_selector do + choose_options(role, nil) + find(member_dropdown_selector).set("#{user2.email} ") + wait_for_requests + + expect(page).to have_content('No matches found') + expect(page).not_to have_button("#{user2.email} ") + end + end + end + context 'when there are multiple users invited with errors' do let_it_be(:user3) { create(:user) } diff --git a/spec/support/shared_examples/features/milestone_editing_shared_examples.rb b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb index d21bf62ecfa..53498a1bb39 100644 --- a/spec/support/shared_examples/features/milestone_editing_shared_examples.rb +++ b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.shared_examples 'milestone handling version conflicts' do - it 'warns about version conflict when milestone has been updated in the background' do + it 'warns about version conflict when milestone has been updated in the background', :js do + wait_for_all_requests + # Update the milestone in the background in order to trigger a version conflict milestone.update!(title: "New title") diff --git a/spec/support/shared_examples/features/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/features/nav_sidebar_shared_examples.rb new file mode 100644 index 00000000000..34821fb9eda --- /dev/null +++ b/spec/support/shared_examples/features/nav_sidebar_shared_examples.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'page has active tab' do |title| + it "activates #{title} tab" do + expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1) + expect(find('.sidebar-top-level-items > li.active')).to have_content(title) + end +end + +RSpec.shared_examples 'page has active sub tab' do |title| + it "activates #{title} sub tab" do + expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1) + expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)')) + .to have_content(title) + end +end diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb index 5126e849c2e..8e8e7e8ad05 100644 --- a/spec/support/shared_examples/features/packages_shared_examples.rb +++ b/spec/support/shared_examples/features/packages_shared_examples.rb @@ -9,7 +9,7 @@ RSpec.shared_examples 'packages list' do |check_project_name: false| expect(package_row).to have_content(pkg.name) expect(package_row).to have_content(pkg.version) - expect(package_row).to have_content(pkg.project.path) if check_project_name + expect(package_row).to have_content(pkg.project.name) if check_project_name 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 7737f8a73c5..806ffdad2f1 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 @@ -4,8 +4,8 @@ RSpec.shared_examples 'it uploads and commits a new text file' do |drop: false| it 'uploads and commits a new text file', :js do find('.add-to-tree').click - page.within('.dropdown-menu') do - click_link('Upload file') + page.within('.repo-breadcrumb') do + click_button('Upload file') wait_for_requests end @@ -40,8 +40,8 @@ RSpec.shared_examples 'it uploads and commits a new image file' do |drop: false| it 'uploads and commits a new image file', :js do find('.add-to-tree').click - page.within('.dropdown-menu') do - click_link('Upload file') + page.within('.repo-breadcrumb') do + click_button('Upload file') wait_for_requests end @@ -70,8 +70,8 @@ RSpec.shared_examples 'it uploads and commits a new pdf file' do |drop: false| it 'uploads and commits a new pdf file', :js do find('.add-to-tree').click - page.within('.dropdown-menu') do - click_link('Upload file') + page.within('.repo-breadcrumb') do + click_button('Upload file') wait_for_requests end @@ -111,7 +111,7 @@ RSpec.shared_examples 'it uploads and commits a new file to a forked project' do wait_for_all_requests find('.add-to-tree').click - click_link('Upload file') + click_button('Upload file') if drop find(".upload-dropzone-card").drop(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) @@ -149,7 +149,7 @@ RSpec.shared_examples 'it uploads a file to a sub-directory' do |drop: false| end find('.add-to-tree').click - click_link('Upload file') + click_button('Upload file') if drop find(".upload-dropzone-card").drop(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) diff --git a/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb b/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb index 337b3f3cbd0..7e3b507c1ba 100644 --- a/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb +++ b/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb @@ -24,6 +24,6 @@ RSpec.shared_examples 'creating an issue for a thread' do expect(discussion.resolved?).to eq(true) # Issue title inludes MR title - expect(page).to have_content(%Q(Follow-up from "#{merge_request.title}")) + expect(page).to have_content(%(Follow-up from "#{merge_request.title}")) end end diff --git a/spec/support/shared_examples/features/rss_shared_examples.rb b/spec/support/shared_examples/features/rss_shared_examples.rb index f6566214e32..a6b9c98923a 100644 --- a/spec/support/shared_examples/features/rss_shared_examples.rb +++ b/spec/support/shared_examples/features/rss_shared_examples.rb @@ -2,20 +2,20 @@ RSpec.shared_examples "an autodiscoverable RSS feed with current_user's feed token" do it "has an RSS autodiscovery link tag with current_user's feed token" do - expect(page).to have_css("link[type*='atom+xml'][href*='feed_token=#{user.feed_token}']", visible: false) + expect(page).to have_css("link[type*='atom+xml'][href*='feed_token=glft-'][href*='-#{user.id}']", visible: false) end end RSpec.shared_examples "it has an RSS button with current_user's feed token" do it "shows the RSS button with current_user's feed token" do expect(page) - .to have_css("a:has([data-testid='rss-icon'])[href*='feed_token=#{user.feed_token}']") + .to have_css("a:has([data-testid='rss-icon'])[href*='feed_token=glft-'][href*='-#{user.id}']") end end RSpec.shared_examples "it has an RSS link with current_user's feed token" do it "shows the RSS link with current_user's feed token" do - expect(page).to have_link 'Subscribe to RSS feed', href: /feed_token=#{user.feed_token}/ + expect(page).to have_link 'Subscribe to RSS feed', href: /feed_token=glft-.*-#{user.id}/ end end @@ -51,11 +51,17 @@ RSpec.shared_examples "updates atom feed link" do |type| auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) expected = { - 'feed_token' => [user.feed_token], 'assignee_id' => [user.id.to_s] } expect(params).to include(expected) + feed_token_param = params['feed_token'] + expect(feed_token_param).to match([Gitlab::Auth::AuthFinders::PATH_DEPENDENT_FEED_TOKEN_REGEX]) + expect(feed_token_param.first).to end_with(user.id.to_s) + expect(auto_discovery_params).to include(expected) + feed_token_param = auto_discovery_params['feed_token'] + expect(feed_token_param).to match([Gitlab::Auth::AuthFinders::PATH_DEPENDENT_FEED_TOKEN_REGEX]) + expect(feed_token_param.first).to end_with(user.id.to_s) end end diff --git a/spec/support/shared_examples/features/sidebar/sidebar_due_date_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_due_date_shared_examples.rb index 206116d66c8..865f5aff476 100644 --- a/spec/support/shared_examples/features/sidebar/sidebar_due_date_shared_examples.rb +++ b/spec/support/shared_examples/features/sidebar/sidebar_due_date_shared_examples.rb @@ -26,7 +26,7 @@ RSpec.shared_examples 'date sidebar widget' do wait_for_requests - expect(page).to have_content(today.to_s(:medium)) + expect(page).to have_content(today.to_fs(:medium)) expect(due_date_value.text).to have_content Time.current.strftime('%b %-d, %Y') end 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 ed885d7a226..c3df89c8002 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 @@ -6,6 +6,7 @@ RSpec.shared_examples 'User creates wiki page' do include WikiHelpers + include ContentEditorHelpers before do sign_in(user) @@ -18,6 +19,7 @@ RSpec.shared_examples 'User creates wiki page' do wait_for_svg_to_be_loaded(example) click_link "Create your first page" + close_rich_text_promo_popover_if_present end it 'shows all available formats in the dropdown' do @@ -190,6 +192,7 @@ RSpec.shared_examples 'User creates wiki page' do context "via the `new wiki page` page", :js do it "creates a page with a single word" do click_link("New page") + close_rich_text_promo_popover_if_present page.within(".wiki-form") do fill_in(:wiki_title, with: "foo") @@ -208,6 +211,7 @@ RSpec.shared_examples 'User creates wiki page' do it "creates a page with spaces in the name", :js do click_link("New page") + close_rich_text_promo_popover_if_present page.within(".wiki-form") do fill_in(:wiki_title, with: "Spaces in the name") @@ -226,6 +230,7 @@ RSpec.shared_examples 'User creates wiki page' do it "creates a page with hyphens in the name", :js do click_link("New page") + close_rich_text_promo_popover_if_present page.within(".wiki-form") do fill_in(:wiki_title, with: "hyphens-in-the-name") @@ -249,6 +254,7 @@ RSpec.shared_examples 'User creates wiki page' do context 'when a server side validation error is returned' do it "still displays edit form", :js do click_link("New page") + close_rich_text_promo_popover_if_present page.within(".wiki-form") do fill_in(:wiki_title, with: "home") @@ -266,6 +272,7 @@ RSpec.shared_examples 'User creates wiki page' do it "shows the emoji autocompletion dropdown", :js do click_link("New page") + close_rich_text_promo_popover_if_present page.within(".wiki-form") do find("#wiki_content").native.send_keys("") diff --git a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb index ca68df9a89b..827c875494a 100644 --- a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb @@ -5,6 +5,8 @@ # user RSpec.shared_examples 'User previews wiki changes' do + include ContentEditorHelpers + let(:wiki_page) { build(:wiki_page, wiki: wiki) } before do @@ -74,6 +76,7 @@ RSpec.shared_examples 'User previews wiki changes' do before do wiki_page.create # rubocop:disable Rails/SaveBang visit wiki_page_path(wiki, wiki_page, action: :edit) + close_rich_text_promo_popover_if_present end it_behaves_like 'relative links' do 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 91cacaf9209..d06f04db1ce 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 @@ -6,6 +6,8 @@ RSpec.shared_examples 'User updates wiki page' do include WikiHelpers + include ContentEditorHelpers + let(:diagramsnet_url) { 'https://embed.diagrams.net' } before do @@ -21,6 +23,7 @@ RSpec.shared_examples 'User updates wiki page' do wait_for_svg_to_be_loaded(example) click_link "Create your first page" + close_rich_text_promo_popover_if_present end it 'redirects back to the home edit page' do @@ -67,6 +70,7 @@ RSpec.shared_examples 'User updates wiki page' do visit(wiki_path(wiki)) click_link('Edit') + close_rich_text_promo_popover_if_present end it 'updates a page', :js do @@ -126,10 +130,6 @@ RSpec.shared_examples 'User updates wiki page' do expect(page).to have_content('Updated Wiki Content') end - it 'focuses on the content field', :js do - expect(page).to have_selector '.note-textarea:focus' - end - it 'cancels editing of a page' do page.within(:css, '.wiki-form .form-actions') do click_on('Cancel') @@ -164,6 +164,7 @@ RSpec.shared_examples 'User updates wiki page' do before do visit wiki_page_path(wiki, wiki_page, action: :edit) + close_rich_text_promo_popover_if_present end it 'moves the page to the root folder', :js do @@ -234,6 +235,7 @@ RSpec.shared_examples 'User updates wiki page' do stub_application_setting(wiki_page_max_content_bytes: 10) visit wiki_page_path(wiki_page.wiki, wiki_page, action: :edit) + close_rich_text_promo_popover_if_present end it 'allows changing the title if the content does not change', :js do diff --git a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb index 767caffd417..3ee7725305e 100644 --- a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb @@ -6,6 +6,7 @@ RSpec.shared_examples 'User views a wiki page' do include WikiHelpers + include ContentEditorHelpers let(:path) { 'image.png' } let(:wiki_page) do @@ -269,6 +270,7 @@ RSpec.shared_examples 'User views a wiki page' do wait_for_svg_to_be_loaded click_link "Create your first page" + close_rich_text_promo_popover_if_present expect(page).to have_content('Create New Page') end diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb index 128bd28410c..4c15b682458 100644 --- a/spec/support/shared_examples/features/work_items_shared_examples.rb +++ b/spec/support/shared_examples/features/work_items_shared_examples.rb @@ -166,7 +166,8 @@ RSpec.shared_examples 'work items comments' do |type| end RSpec.shared_examples 'work items assignees' do - it 'successfully assigns the current user by searching' do + it 'successfully assigns the current user by searching', + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/413074' do # The button is only when the mouse is over the input find('[data-testid="work-item-assignees-input"]').fill_in(with: user.username) wait_for_requests diff --git a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb index 5cbbed1468f..38954e6f9cc 100644 --- a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb +++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb @@ -42,6 +42,12 @@ RSpec.shared_examples 'no assignee filter' do expect(issuables).to contain_exactly(*expected_issuables) end + + it 'returns issuables not assigned to any assignee' do + params[:assignee_wildcard_id] = 'none' + + expect(issuables).to contain_exactly(*expected_issuables) + end end RSpec.shared_examples 'any assignee filter' do @@ -57,5 +63,11 @@ RSpec.shared_examples 'any assignee filter' do expect(issuables).to contain_exactly(*expected_issuables) end + + it 'returns issuables assigned to any assignee' do + params[:assignee_wildcard_id] = 'any' + + expect(issuables).to contain_exactly(*expected_issuables) + end end end diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb index 67fed00b5ca..30041456d00 100644 --- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb +++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb @@ -663,18 +663,6 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context expect(items).to contain_exactly(japanese) end end - - context 'when full-text search is disabled' do - let(:search_term) { 'ometh' } - - before do - stub_feature_flags(issues_full_text_search: false) - end - - it 'allows partial word matches' do - expect(items).to contain_exactly(english) - end - end end context 'filtering by item term in title' do diff --git a/spec/support/shared_examples/graphql/label_fields.rb b/spec/support/shared_examples/graphql/label_fields.rb index 030a2feafcd..809c801de62 100644 --- a/spec/support/shared_examples/graphql/label_fields.rb +++ b/spec/support/shared_examples/graphql/label_fields.rb @@ -93,7 +93,7 @@ RSpec.shared_examples 'querying a GraphQL type with labels' do describe 'performance' do def query_for(*labels) selections = labels.map do |label| - %Q[#{label.title.gsub(/:+/, '_')}: label(title: "#{label.title}") { description }] + %[#{label.title.gsub(/:+/, '_')}: label(title: "#{label.title}") { description }] end make_query(selections) diff --git a/spec/support/shared_examples/graphql/mutations/boards_list_create_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/boards_list_create_shared_examples.rb index b096a5e17c0..13d2447754c 100644 --- a/spec/support/shared_examples/graphql/mutations/boards_list_create_shared_examples.rb +++ b/spec/support/shared_examples/graphql/mutations/boards_list_create_shared_examples.rb @@ -33,6 +33,10 @@ RSpec.shared_examples 'board lists create mutation' do describe 'backlog list' do let(:list_create_params) { { backlog: true } } + before do + board.lists.backlog.delete_all + end + it 'creates one and only one backlog' do expect { subject }.to change { board.lists.backlog.count }.by(1) expect(board.lists.backlog.first.list_type).to eq 'backlog' diff --git a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb index 30212e44c6a..c666b72d492 100644 --- a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb +++ b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb @@ -157,10 +157,10 @@ RSpec.shared_examples 'work item supports type change via quick actions' do let_it_be(:assignee) { create(:user) } let_it_be(:task_type) { WorkItems::Type.default_by_type(:task) } - let(:body) { "Updating type.\n/type Issue" } + let(:body) { "Updating type.\n/type issue" } before do - noteable.update!(work_item_type: task_type, issue_type: task_type.base_type) + noteable.update!(work_item_type: task_type) end it 'updates type' do @@ -211,4 +211,15 @@ RSpec.shared_examples 'work item supports type change via quick actions' do .to include("Commands only Type changed successfully. Assigned @#{assignee.username}.") end end + + context 'when the type name is upper case' do + let(:body) { "Updating type.\n/type Issue" } + + it 'changes type to issue' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.to change { noteable.work_item_type.base_type }.from('task').to('issue') + end + end end diff --git a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb index d8cc6f697d7..c32e758d921 100644 --- a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb +++ b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb @@ -50,6 +50,8 @@ RSpec.shared_examples "a user type with merge request interaction type" do organization jobTitle createdAt + pronouns + ide ] # TODO: 'workspaces' needs to be included, but only when this spec is run in EE context, to account for the diff --git a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb index 47655f86558..e6433f963f4 100644 --- a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb +++ b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'default allowlist' do expect(filter(act).to_html).to eq exp end - it 'allows whitelisted HTML tags from the user' do + it 'allows allowlisted HTML tags from the user' do exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>" expect(filter(act).to_html).to eq exp end @@ -110,7 +110,7 @@ RSpec.shared_examples 'XSS prevention' do }, 'protocol-based JS injection: Unicode' => { - input: %Q(<a href="\u0001java\u0003script:alert('XSS')">foo</a>), + input: %(<a href="\u0001java\u0003script:alert('XSS')">foo</a>), output: '<a>foo</a>' }, diff --git a/spec/support/shared_examples/lib/gitlab/database/foreign_key_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/foreign_key_validators_shared_examples.rb deleted file mode 100644 index a1e75e4af7e..00000000000 --- a/spec/support/shared_examples/lib/gitlab/database/foreign_key_validators_shared_examples.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.shared_examples 'foreign key validators' do |validator, expected_result| - subject(:result) { validator.new(structure_file, database).execute } - - let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') } - let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) } - let(:inconsistency_type) { validator.name.demodulize.underscore } - let(:database_name) { 'main' } - let(:schema) { 'public' } - let(:database_model) { Gitlab::Database.database_base_models[database_name] } - let(:connection) { database_model.connection } - let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) } - - let(:database_query) do - [ - { - 'schema' => schema, - 'table_name' => 'web_hooks', - 'foreign_key_name' => 'web_hooks_project_id_fkey', - 'foreign_key_definition' => 'FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE' - }, - { - 'schema' => schema, - 'table_name' => 'issues', - 'foreign_key_name' => 'wrong_definition_fk', - 'foreign_key_definition' => 'FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE' - }, - { - 'schema' => schema, - 'table_name' => 'projects', - 'foreign_key_name' => 'extra_fk', - 'foreign_key_definition' => 'FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE' - } - ] - end - - before do - allow(connection).to receive(:exec_query).and_return(database_query) - end - - it 'returns trigger inconsistencies' do - expect(result.map(&:object_name)).to match_array(expected_result) - expect(result.map(&:type)).to all(eql inconsistency_type) - end -end diff --git a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb deleted file mode 100644 index 6f0cede7130..00000000000 --- a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.shared_examples "index validators" do |validator, expected_result| - let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') } - let(:database_indexes) do - [ - ['wrong_index', 'CREATE UNIQUE INDEX wrong_index ON public.table_name (column_name)'], - ['extra_index', 'CREATE INDEX extra_index ON public.table_name (column_name)'], - ['index', 'CREATE UNIQUE INDEX "index" ON public.achievements USING btree (namespace_id, lower(name))'] - ] - end - - let(:inconsistency_type) { validator.name.demodulize.underscore } - - let(:database_name) { 'main' } - - let(:database_model) { Gitlab::Database.database_base_models[database_name] } - - let(:connection) { database_model.connection } - - let(:schema) { connection.current_schema } - - let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) } - let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) } - - subject(:result) { validator.new(structure_file, database).execute } - - before do - allow(connection).to receive(:select_rows).and_return(database_indexes) - end - - it 'returns index inconsistencies' do - expect(result.map(&:object_name)).to match_array(expected_result) - expect(result.map(&:type)).to all(eql inconsistency_type) - end -end diff --git a/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb deleted file mode 100644 index 96e58294675..00000000000 --- a/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.shared_examples "table validators" do |validator, expected_result| - subject(:result) { validator.new(structure_file, database).execute } - - let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') } - let(:inconsistency_type) { validator.name.demodulize.underscore } - let(:database_model) { Gitlab::Database.database_base_models['main'] } - let(:connection) { database_model.connection } - let(:schema) { connection.current_schema } - let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) } - let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) } - let(:database_tables) do - [ - { - 'table_name' => 'wrong_table', - 'column_name' => 'id', - 'not_null' => true, - 'data_type' => 'integer', - 'column_default' => "nextval('audit_events_id_seq'::regclass)" - }, - { - 'table_name' => 'wrong_table', - 'column_name' => 'description', - 'not_null' => true, - 'data_type' => 'character varying', - 'column_default' => nil - }, - { - 'table_name' => 'extra_table', - 'column_name' => 'id', - 'not_null' => true, - 'data_type' => 'integer', - 'column_default' => "nextval('audit_events_id_seq'::regclass)" - }, - { - 'table_name' => 'extra_table', - 'column_name' => 'email', - 'not_null' => true, - 'data_type' => 'character varying', - 'column_default' => nil - }, - { - 'table_name' => 'extra_table_columns', - 'column_name' => 'id', - 'not_null' => true, - 'data_type' => 'bigint', - 'column_default' => "nextval('audit_events_id_seq'::regclass)" - }, - { - 'table_name' => 'extra_table_columns', - 'column_name' => 'name', - 'not_null' => true, - 'data_type' => 'character varying(255)', - 'column_default' => nil - }, - { - 'table_name' => 'extra_table_columns', - 'column_name' => 'extra_column', - 'not_null' => true, - 'data_type' => 'character varying(255)', - 'column_default' => nil - }, - { - 'table_name' => 'missing_table_columns', - 'column_name' => 'id', - 'not_null' => true, - 'data_type' => 'bigint', - 'column_default' => 'NOT NULL' - } - ] - end - - before do - allow(connection).to receive(:exec_query).and_return(database_tables) - end - - it 'returns table inconsistencies' do - expect(result.map(&:object_name)).to match_array(expected_result) - expect(result.map(&:type)).to all(eql inconsistency_type) - end -end diff --git a/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb deleted file mode 100644 index 13a112275c2..00000000000 --- a/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.shared_examples 'trigger validators' do |validator, expected_result| - subject(:result) { validator.new(structure_file, database).execute } - - let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') } - let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) } - let(:inconsistency_type) { validator.name.demodulize.underscore } - let(:database_name) { 'main' } - let(:schema) { 'public' } - let(:database_model) { Gitlab::Database.database_base_models[database_name] } - let(:connection) { database_model.connection } - let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) } - - let(:database_triggers) do - [ - ['trigger', 'CREATE TRIGGER trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION t1()'], - ['wrong_trigger', 'CREATE TRIGGER wrong_trigger BEFORE UPDATE ON public.t2 FOR EACH ROW EXECUTE FUNCTION t2()'], - ['extra_trigger', 'CREATE TRIGGER extra_trigger BEFORE INSERT ON public.t4 FOR EACH ROW EXECUTE FUNCTION t4()'] - ] - end - - before do - allow(connection).to receive(:select_rows).and_return(database_triggers) - end - - it 'returns trigger inconsistencies' do - expect(result.map(&:object_name)).to match_array(expected_result) - expect(result.map(&:type)).to all(eql inconsistency_type) - end -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 169fceced7a..9dc18555340 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 @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples 'tracked issuable snowplow and service ping events for given event params' do +RSpec.shared_examples 'tracked issuable events' do before do stub_application_setting(usage_ping_enabled: true) end @@ -21,6 +21,10 @@ RSpec.shared_examples 'tracked issuable snowplow and service ping events for giv it 'does not track edit actions if author is not present' do expect(track_action({ author: nil }.merge(track_params))).to be_nil end +end + +RSpec.shared_examples 'tracked issuable snowplow and service ping events for given event params' do + it_behaves_like 'tracked issuable events' it 'emits snowplow event' do track_action({ author: user1 }.merge(track_params)) @@ -29,6 +33,23 @@ RSpec.shared_examples 'tracked issuable snowplow and service ping events for giv end end +RSpec.shared_examples 'tracked issuable internal event for given event params' do + it_behaves_like 'tracked issuable events' + + it_behaves_like 'internal event tracking' do + subject(:track_event) { track_action({ author: user1 }.merge(track_params)) } + + let(:user) { user1 } + let(:namespace) { project&.namespace } + end +end + +RSpec.shared_examples 'tracked issuable internal event with project' do + it_behaves_like 'tracked issuable internal event for given event params' do + let(:track_params) { original_params || { project: project } } + end +end + RSpec.shared_examples 'tracked issuable snowplow and service ping events with project' do it_behaves_like 'tracked issuable snowplow and service ping events for given event params' do let(:context) do diff --git a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb index 6f104f400bc..52f0e7847b0 100644 --- a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb +++ b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb @@ -2,7 +2,7 @@ RSpec.shared_examples 'StageEventModel' do describe '.upsert_data' do - let(:time) { Time.parse(Time.current.to_s(:db)) } # truncating the timestamp so we can compare it with the timestamp loaded from the DB + let(:time) { Time.parse(Time.current.to_fs(:db)) } # truncating the timestamp so we can compare it with the timestamp loaded from the DB let(:input_data) do [ { diff --git a/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb b/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb index 4753d7a4556..0e9200f1fd9 100644 --- a/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb +++ b/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb @@ -6,16 +6,34 @@ RSpec.shared_examples 'protected ref access' do |association| let_it_be(:project) { create(:project) } let_it_be(:protected_ref) { create(association, project: project) } # rubocop:disable Rails/SaveBang - it { is_expected.to validate_inclusion_of(:access_level).in_array(described_class.allowed_access_levels) } + describe 'validations' do + subject { build(described_class.model_name.singular) } - it { is_expected.to validate_presence_of(:access_level) } + context 'when role?' do + it { is_expected.to validate_inclusion_of(:access_level).in_array(described_class.allowed_access_levels) } - context 'when not role?' do - before do - allow(subject).to receive(:role?).and_return(false) + it { is_expected.to validate_presence_of(:access_level) } + + it do + is_expected.to validate_uniqueness_of(:access_level) + .scoped_to("#{described_class.module_parent.model_name.singular}_id") + end end - it { is_expected.not_to validate_presence_of(:access_level) } + context 'when not role?' do + before do + allow(subject).to receive(:role?).and_return(false) + end + + it { is_expected.not_to validate_presence_of(:access_level) } + + it { is_expected.not_to validate_inclusion_of(:access_level).in_array(described_class.allowed_access_levels) } + + it do + is_expected.not_to validate_uniqueness_of(:access_level) + .scoped_to("#{described_class.module_parent.model_name.singular}_id") + end + end end describe '::human_access_levels' do diff --git a/spec/support/shared_examples/models/concerns/protected_ref_deploy_key_access_examples.rb b/spec/support/shared_examples/models/concerns/protected_ref_deploy_key_access_examples.rb new file mode 100644 index 00000000000..f2e79dc377b --- /dev/null +++ b/spec/support/shared_examples/models/concerns/protected_ref_deploy_key_access_examples.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'protected ref deploy_key access' do + let_it_be(:described_instance) { described_class.model_name.singular } + let_it_be(:protected_ref_name) { described_class.module_parent.model_name.singular } + let_it_be(:project) { create(:project) } + let_it_be(:protected_ref) { create(protected_ref_name, project: project) } # rubocop:disable Rails/SaveBang + + describe 'associations' do + it { is_expected.to belong_to(:deploy_key) } + end + + describe 'validations' do + context 'when deploy_key?' do + context 'when deploy key enabled for the project' do + let(:deploy_key) do + create(:deploy_keys_project, :write_access, project: project).deploy_key + end + + it 'is valid' do + level = build(described_instance, protected_ref_name => protected_ref, deploy_key: deploy_key) + + expect(level).to be_valid + end + end + + context 'when a deploy key already added for this access level' do + let(:deploy_key) { create(:deploy_keys_project, :write_access, project: project).deploy_key } + + before do + create(described_instance, protected_ref_name => protected_ref, deploy_key: deploy_key) + end + + subject(:access_level) do + build(described_instance, protected_ref_name => protected_ref, deploy_key: deploy_key) + end + + it 'is not valid', :aggregate_failures do + is_expected.to be_invalid + expect(access_level.errors.full_messages).to contain_exactly('Deploy key has already been taken') + end + end + + context 'when deploy key is not enabled for the project' do + subject(:access_level) do + build(described_instance, protected_ref_name => protected_ref, deploy_key: create(:deploy_key)) + end + + it 'is not valid', :aggregate_failures do + is_expected.to be_invalid + expect(access_level.errors.full_messages).to contain_exactly('Deploy key is not enabled for this project') + end + end + + context 'when deploy key is not active for the project' do + subject(:access_level) do + deploy_key = create(:deploy_keys_project, :readonly_access, project: project).deploy_key + build(described_instance, protected_ref_name => protected_ref, deploy_key: deploy_key) + end + + it 'is not valid', :aggregate_failures do + is_expected.to be_invalid + expect(access_level.errors.full_messages).to contain_exactly('Deploy key is not enabled for this project') + end + end + end + end + + describe '#check_access' do + let_it_be(:user) { create(:user) } + let_it_be(:deploy_key) { create(:deploy_key, user: user) } + let_it_be(:deploy_keys_project) do + create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) + end + + before_all do + project.add_maintainer(user) + end + + context "when this #{described_class.model_name.singular} is tied to a deploy key" do + let!(:access_level) do + create(described_instance, protected_ref_name => protected_ref, deploy_key: deploy_key) + end + + context 'when the deploy key is among the active keys for this project' do + it { expect(access_level.check_access(user)).to be_truthy } + end + + context 'when user is missing' do + it { expect(access_level.check_access(nil)).to be_falsey } + end + + context 'when deploy key does not belong to the user' do + let(:another_user) { create(:user) } + + it { expect(access_level.check_access(another_user)).to be_falsey } + end + + context 'when user cannot access the project' do + before do + allow(user).to receive(:can?).with(:read_project, project).and_return(false) + end + + it { expect(access_level.check_access(user)).to be_falsey } + end + + context 'when the deploy key is not among the active keys of this project' do + before do + deploy_keys_project.update!(can_push: false) + end + + after do + deploy_keys_project.update!(can_push: true) + end + + it { expect(access_level.check_access(user)).to be_falsey } + end + end + end + + describe '#type' do + let(:access_level) { build(described_instance) } + + context 'when deploy_key?' do + let(:access_level) { build(described_instance, deploy_key: build(:deploy_key)) } + + it 'returns :deploy_key' do + expect(access_level.type).to eq(:deploy_key) + end + end + end +end diff --git a/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb b/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb index 3d98d9136e2..56b36b3ea07 100644 --- a/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb +++ b/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb @@ -12,7 +12,6 @@ RSpec.shared_examples 'database events tracking' do let(:category) { described_class.to_s } let(:label) { described_class.table_name } let(:action) { "database_event_#{property}" } - let(:feature_flag_name) { :product_intelligence_database_event_tracking } let(:record_tracked_attributes) { record.attributes.slice(*described_class::SNOWPLOW_ATTRIBUTES.map(&:to_s)) } let(:base_extra) { record_tracked_attributes.merge(project: try(:project), namespace: try(:namespace)) } @@ -48,9 +47,3 @@ RSpec.shared_examples 'database events tracking' do end end end - -RSpec.shared_examples 'database events tracking batch 2' do - it_behaves_like 'database events tracking' do - let(:feature_flag_name) { :product_intelligence_database_event_tracking_batch2 } - end -end diff --git a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb index 0cf109ce5c5..ff3cc1841b4 100644 --- a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb +++ b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb @@ -122,7 +122,7 @@ RSpec.shared_examples Integrations::BaseSlashCommands do end it_behaves_like 'blocks command execution' do - let(:error_message) { 'your account has been deactivated by your administrator' } + let(:error_message) { "your #{Gitlab.config.gitlab.url} account needs to be reactivated" } end end end diff --git a/spec/support/shared_examples/namespaces/traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb index 600539f7d0a..4dff4f68995 100644 --- a/spec/support/shared_examples/namespaces/traversal_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_examples.rb @@ -239,13 +239,11 @@ RSpec.shared_examples 'namespace traversal' do end describe '#ancestors_upto' do - context 'with use_traversal_ids_for_ancestors_upto enabled' do - include_examples '#ancestors_upto' - end + include_examples '#ancestors_upto' - context 'with use_traversal_ids_for_ancestors_upto disabled' do + context 'with use_traversal_ids disabled' do before do - stub_feature_flags(use_traversal_ids_for_ancestors_upto: false) + stub_feature_flags(use_traversal_ids: false) end include_examples '#ancestors_upto' diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index 0c4e5ce51fc..b308295b5fb 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -70,10 +70,9 @@ RSpec.shared_examples 'namespace traversal scopes' do end describe '.roots' do - context "use_traversal_ids_roots feature flag is true" do + context "use_traversal_ids feature flag is true" do before do stub_feature_flags(use_traversal_ids: true) - stub_feature_flags(use_traversal_ids_roots: true) end it_behaves_like '.roots' @@ -83,9 +82,9 @@ RSpec.shared_examples 'namespace traversal scopes' do end end - context "use_traversal_ids_roots feature flag is false" do + context "use_traversal_ids feature flag is false" do before do - stub_feature_flags(use_traversal_ids_roots: false) + stub_feature_flags(use_traversal_ids: false) end it_behaves_like '.roots' diff --git a/spec/support/shared_examples/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/nav_sidebar_shared_examples.rb deleted file mode 100644 index 4b815988bc5..00000000000 --- a/spec/support/shared_examples/nav_sidebar_shared_examples.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'has nav sidebar' do - it 'has collapsed nav sidebar on mobile' do - render - - expect(rendered).to have_selector('.nav-sidebar') - expect(rendered).not_to have_selector('.sidebar-collapsed-desktop') - expect(rendered).not_to have_selector('.sidebar-expanded-mobile') - end -end - -RSpec.shared_examples 'page has active tab' do |title| - it "activates #{title} tab" do - expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1) - expect(find('.sidebar-top-level-items > li.active')).to have_content(title) - end -end - -RSpec.shared_examples 'page has active sub tab' do |title| - it "activates #{title} sub tab" do - expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1) - expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)')) - .to have_content(title) - end -end - -RSpec.shared_examples 'sidebar includes snowplow attributes' do |track_action, track_label, track_property| - specify do - stub_application_setting(snowplow_enabled: true) - - render - - expect(rendered).to have_css(".nav-sidebar[data-track-action=\"#{track_action}\"][data-track-label=\"#{track_label}\"][data-track-property=\"#{track_property}\"]") - end -end diff --git a/spec/support/shared_examples/npm_sync_metadata_cache_worker_shared_examples.rb b/spec/support/shared_examples/npm_sync_metadata_cache_worker_shared_examples.rb new file mode 100644 index 00000000000..de2dc4c3725 --- /dev/null +++ b/spec/support/shared_examples/npm_sync_metadata_cache_worker_shared_examples.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'does not enqueue a worker to sync a metadata cache' do + it 'does not enqueue a worker to sync a metadata cache' do + expect(Packages::Npm::CreateMetadataCacheWorker).not_to receive(:perform_async) + + subject + end +end + +RSpec.shared_examples 'enqueue a worker to sync a metadata cache' do + before do + project.add_maintainer(user) + end + + it 'enqueues a worker to create a metadata cache' do + expect(Packages::Npm::CreateMetadataCacheWorker) + .to receive(:perform_async).with(project.id, package_name) + + subject + end + + context 'with npm_metadata_cache disabled' do + before do + stub_feature_flags(npm_metadata_cache: false) + end + + it_behaves_like 'does not enqueue a worker to sync a metadata cache' + end +end diff --git a/spec/support/shared_examples/observability/csp_shared_examples.rb b/spec/support/shared_examples/observability/csp_shared_examples.rb index 9d6e7e75f4d..9002ccd5878 100644 --- a/spec/support/shared_examples/observability/csp_shared_examples.rb +++ b/spec/support/shared_examples/observability/csp_shared_examples.rb @@ -5,23 +5,28 @@ # It requires the following variables declared in the context including this example: # # - `tested_path`: the path under test -# - `user`: the test user -# - `group`: the test group # # e.g. # # ``` -# let_it_be(:group) { create(:group) } -# let_it_be(:user) { create(:user) } # it_behaves_like "observability csp policy" do # let(:tested_path) { ....the path under test } # end # ``` # +# Note that the context's user is expected to be logged-in and the +# related resources (group, project, etc) are supposed to be provided with proper +# permissions already, e.g. +# +# before do +# login_as(user) +# group.add_developer(user) +# end +# # It optionally supports specifying the controller class handling the tested path as a parameter, e.g. # # ``` -# it_behaves_like "observability csp policy", Groups::ObservabilityController +# it_behaves_like "observability csp policy", Projects::TracingController # ``` # (If not specified it will default to `described_class`) # @@ -41,9 +46,6 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe before do setup_csp_for_controller(controller_class, csp, any_time: true) - group.add_developer(user) - login_as(user) - stub_feature_flags(observability_group_tab: true) end subject do @@ -59,93 +61,127 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe end end - context 'when observability is disabled' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src 'https://something.test' + describe 'frame-src' do + context 'when frame-src exists in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.frame_src 'https://something.test' + end end - end - before do - stub_feature_flags(observability_group_tab: false) + it 'appends the proper url to frame-src CSP directives' do + expect(subject).to include( + "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") + end end - it 'does not add observability urls to the csp header' do - expect(subject).to include("frame-src https://something.test") - expect(subject).not_to include("#{observability_url} #{signin_url} #{oauth_url}") - end - end + context 'when signin url is already present in the policy' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.frame_src signin_url + end + end - context 'when frame-src exists in the CSP config' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src 'https://something.test' + it 'does not append signin again' do + expect(subject).to include( + "frame-src #{signin_url} #{observability_url} #{oauth_url};") end end - it 'appends the proper url to frame-src CSP directives' do - expect(subject).to include( - "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") - end - end + context 'when oauth url is already present in the policy' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.frame_src oauth_url + end + end - context 'when signin is already present in the policy' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src signin_url + it 'does not append oauth again' do + expect(subject).to include( + "frame-src #{oauth_url} #{observability_url} #{signin_url};") end end - it 'does not append signin again' do - expect(subject).to include( - "frame-src #{signin_url} #{observability_url} #{oauth_url};") - end - end + context 'when default-src exists in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src 'https://something.test' + end + end + + it 'does not change default-src' do + expect(subject).to include( + "default-src https://something.test;") + end - context 'when oauth is already present in the policy' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.frame_src oauth_url + it 'appends the proper url to frame-src CSP directives' do + expect(subject).to include( + "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") end end - it 'does not append oauth again' do - expect(subject).to include( - "frame-src #{oauth_url} #{observability_url} #{signin_url};") + context 'when frame-src and default-src exist in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src 'https://something_default.test' + p.frame_src 'https://something.test' + end + end + + it 'appends to frame-src CSP directives' do + expect(subject).to include( + "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") + expect(subject).to include( + "default-src https://something_default.test") + end end end - context 'when default-src exists in the CSP config' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.default_src 'https://something.test' + describe 'connect-src' do + context 'when connect-src exists in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.connect_src 'https://something.test' + end end - end - it 'does not change default-src' do - expect(subject).to include( - "default-src https://something.test;") + it 'appends the proper url to connect-src CSP directives' do + expect(subject).to include( + "connect-src https://something.test localhost #{observability_url}") + end end - it 'appends the proper url to frame-src CSP directives' do - expect(subject).to include( - "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") - end - end + context 'when default-src exists in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src 'https://something.test' + end + end + + it 'does not change default-src' do + expect(subject).to include( + "default-src https://something.test;") + end - context 'when frame-src and default-src exist in the CSP config' do - let(:csp) do - ActionDispatch::ContentSecurityPolicy.new do |p| - p.default_src 'https://something_default.test' - p.frame_src 'https://something.test' + it 'appends the proper url to connect-src CSP directives' do + expect(subject).to include( + "connect-src https://something.test localhost #{observability_url}") end end - it 'appends to frame-src CSP directives' do - expect(subject).to include( - "frame-src https://something.test #{observability_url} #{signin_url} #{oauth_url}") - expect(subject).to include( - "default-src https://something_default.test") + context 'when connect-src and default-src exist in the CSP config' do + let(:csp) do + ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src 'https://something_default.test' + p.connect_src 'https://something.test' + end + end + + it 'appends to connect-src CSP directives' do + expect(subject).to include( + "connect-src https://something.test localhost #{observability_url}") + expect(subject).to include( + "default-src https://something_default.test") + end end end end diff --git a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb index 7cbaf40721a..4b27f1f2520 100644 --- a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb @@ -2,6 +2,7 @@ RSpec.shared_examples 'close quick action' do |issuable_type| include Features::NotesHelpers + include ContentEditorHelpers before do project.add_maintainer(maintainer) @@ -76,6 +77,7 @@ RSpec.shared_examples 'close quick action' do |issuable_type| context "preview of note on #{issuable_type}", :js do it 'explains close quick action' do visit public_send("project_#{issuable_type}_path", project, issuable) + close_rich_text_promo_popover_if_present preview_note("this is done, close\n/close") do expect(page).not_to have_content '/close' diff --git a/spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb index acbc6429646..cf5a67f6096 100644 --- a/spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/merge_request/merge_quick_action_shared_examples.rb @@ -41,7 +41,13 @@ RSpec.shared_examples 'merge quick action' do it 'schedules to merge the MR' do add_note("/merge") - expect(page).to have_content "Scheduled to merge this merge request (Merge when pipeline succeeds)." + if Gitlab.ee? + expect(page).to( + have_content("Scheduled to merge this merge request (Merge when checks pass).") + ) + else + expect(page).to have_content "Scheduled to merge this merge request (Merge when pipeline succeeds)." + end expect(merge_request.reload).to be_auto_merge_enabled expect(merge_request.reload).not_to be_merged diff --git a/spec/support/shared_examples/quick_actions/work_item/type_change_quick_actions_shared_examples.rb b/spec/support/shared_examples/quick_actions/work_item/type_change_quick_actions_shared_examples.rb index 9ccb7c0ae42..0fc914d71d5 100644 --- a/spec/support/shared_examples/quick_actions/work_item/type_change_quick_actions_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/work_item/type_change_quick_actions_shared_examples.rb @@ -20,7 +20,7 @@ RSpec.shared_examples 'quick actions that change work item type' do end context 'when new type is the same as current type' do - let(:command) { '/type Issue' } + let(:command) { '/type issue' } it_behaves_like 'quick command error', 'Types are the same' end @@ -57,7 +57,7 @@ RSpec.shared_examples 'quick actions that change work item type' do it 'populates :issue_type: and :work_item_type' do _, updates, message = service.execute(command, work_item) - expect(message).to eq(_('Work Item promoted successfully.')) + expect(message).to eq(_('Work item promoted successfully.')) expect(updates).to eq({ issue_type: 'incident', work_item_type: WorkItems::Type.default_by_type(:incident) }) end @@ -73,7 +73,7 @@ RSpec.shared_examples 'quick actions that change work item type' do it 'populates :issue_type: and :work_item_type' do _, updates, message = service.execute(command, work_item) - expect(message).to eq(_('Work Item promoted successfully.')) + expect(message).to eq(_('Work item promoted successfully.')) expect(updates).to eq({ issue_type: 'issue', work_item_type: WorkItems::Type.default_by_type(:issue) }) end diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb index 5cb6c3d310f..513f9802b34 100644 --- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb @@ -20,7 +20,6 @@ RSpec.shared_examples 'Debian packages upload request' do |status, body = nil| if status == :created it 'creates package files', :aggregate_failures do expect(::Packages::Debian::CreatePackageFileService).to receive(:new).with(package: be_a(Packages::Package), current_user: be_an(User), params: be_an(Hash)).and_call_original - expect(::Packages::Debian::ProcessChangesWorker).not_to receive(:perform_async) if extra_params[:distribution] || file_name.end_with?('.changes') expect(::Packages::Debian::FindOrCreateIncomingService).not_to receive(:new) diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb index 930c47dac52..6eceb7c350d 100644 --- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb @@ -17,6 +17,10 @@ RSpec.shared_examples 'graphql issue list request spec' do end describe 'filters' do + let(:mutually_exclusive_error) do + 'only one of [assigneeUsernames, assigneeUsername, assigneeWildcardId] arguments is allowed at the same time.' + end + before_all do issue_a.assignee_ids = current_user.id issue_b.assignee_ids = another_user.id @@ -31,9 +35,45 @@ RSpec.shared_examples 'graphql issue list request spec' do it 'returns a mutually exclusive param error' do post_query - expect_graphql_errors_to_include( - 'only one of [assigneeUsernames, assigneeUsername] arguments is allowed at the same time.' - ) + expect_graphql_errors_to_include(mutually_exclusive_error) + end + end + + context 'when both assignee_username and assignee_wildcard_id filters are provided' do + let(:issue_filter_params) do + { assignee_username: current_user.username, assignee_wildcard_id: :ANY } + end + + it 'returns a mutually exclusive param error' do + post_query + + expect_graphql_errors_to_include(mutually_exclusive_error) + end + end + + context 'when filtering by assignee_wildcard_id' do + context 'when filtering for all issues with assignees' do + let(:issue_filter_params) do + { assignee_wildcard_id: :ANY } + end + + it 'returns all issues with assignees' do + post_query + + expect(issue_ids).to match_array([issue_a, issue_b].map { |i| i.to_gid.to_s }) + end + end + + context 'when filtering for issues without assignees' do + let(:issue_filter_params) do + { assignee_wildcard_id: :NONE } + end + + it 'returns all issues without assignees' do + post_query + + expect(issue_ids).to match_array([issue_c, issue_d, issue_e].map { |i| i.to_gid.to_s }) + end end end @@ -492,7 +532,7 @@ RSpec.shared_examples 'graphql issue list request spec' do end before do - issue_a.update_columns(issue_type: WorkItems::Type.base_types[:incident], work_item_type_id: incident_type.id) + issue_a.update_columns(work_item_type_id: incident_type.id) end it 'returns the escalation status values' do diff --git a/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb new file mode 100644 index 00000000000..7c32c7bf2a9 --- /dev/null +++ b/spec/support/shared_examples/requests/api/graphql/remote_development_shared_examples.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'workspaces query in licensed environment and with feature flag on' do + describe 'when licensed and remote_development_feature_flag feature flag is enabled' do + before do + stub_licensed_features(remote_development: true) + + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + # noinspection RubyResolve + it { is_expected.to match_array(a_hash_including('name' => workspace.name)) } + # noinspection RubyResolve + + context 'when user is not authorized' do + let(:current_user) { create(:user) } + + it { is_expected.to eq([]) } + end + end +end + +RSpec.shared_examples 'workspaces query in unlicensed environment and with feature flag off' do + describe 'when remote_development feature is unlicensed' do + before do + stub_licensed_features(remote_development: false) + post_graphql(query, current_user: current_user) + end + + it 'returns an error' do + expect(subject).to be_nil + expect_graphql_errors_to_include(/'remote_development' licensed feature is not available/) + end + end + + describe 'when remote_development_feature_flag feature flag is disabled' do + before do + stub_licensed_features(remote_development: true) + stub_feature_flags(remote_development_feature_flag: false) + post_graphql(query, current_user: current_user) + end + + it 'returns an error' do + expect(subject).to be_nil + expect_graphql_errors_to_include(/'remote_development_feature_flag' feature flag is disabled/) + end + end +end diff --git a/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb index 81ff004779a..47cbd268a65 100644 --- a/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb @@ -27,7 +27,7 @@ RSpec.shared_examples 'creates model experiments package files' do end it 'returns bad request if package creation fails' do - allow_next_instance_of(::Packages::MlModel::CreatePackageFileService) do |instance| + expect_next_instance_of(::Packages::MlModel::CreatePackageFileService) do |instance| expect(instance).to receive(:execute).and_return(nil) end @@ -106,3 +106,19 @@ RSpec.shared_examples 'process ml model package upload' do end end end + +RSpec.shared_examples 'process ml model package download' do + context 'when package file exists' do + it { is_expected.to have_gitlab_http_status(:success) } + end + + context 'when record does not exist' do + it 'response is not found' do + expect_next_instance_of(::Packages::MlModel::PackageFinder) do |instance| + expect(instance).to receive(:execute!).and_raise(ActiveRecord::RecordNotFound) + end + + expect(api_response).to have_gitlab_http_status(:not_found) + end + end +end diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb index 5284ed2de21..6b6bf375827 100644 --- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb @@ -44,11 +44,7 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project| end shared_examples 'reject metadata request' do |status:| - it 'rejects the metadata request' do - subject - - expect(response).to have_gitlab_http_status(status) - end + it_behaves_like 'returning response status', status end shared_examples 'redirect metadata request' do |status:| @@ -280,13 +276,15 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project| example_name = 'redirect metadata request' status = :redirected else - example_name = 'reject metadata request' status = :not_found end end status = :not_found if scope == :group && params[:package_name_type] == :non_existing && !params[:request_forward] + # Check the error message for :not_found + example_name = 'returning response status with error' if status == :not_found + it_behaves_like example_name, status: status end end @@ -361,11 +359,11 @@ RSpec.shared_examples 'handling audit request' do |path:, scope: :project| end shared_examples 'reject audit request' do |status:| - it 'rejects the audit request' do - subject + it_behaves_like 'returning response status', status + end - expect(response).to have_gitlab_http_status(status) - end + shared_examples 'reject audit request with error' do |status:| + it_behaves_like 'returning response status with error', status: status, error: 'Project not found' end shared_examples 'redirect audit request' do |status:| @@ -464,7 +462,7 @@ RSpec.shared_examples 'handling audit request' do |path:, scope: :project| example_name = 'redirect audit request' status = :temporary_redirect else - example_name = 'reject audit request' + example_name = 'reject audit request with error' status = :not_found end end @@ -633,12 +631,12 @@ RSpec.shared_examples 'handling get dist tags requests' do |scope: :project| example_name = "#{params[:expected_result]} package tags request" status = params[:expected_status] - if scope == :instance && params[:package_name_type] != :scoped_naming_convention - example_name = 'reject package tags request' + if (scope == :instance && params[:package_name_type] != :scoped_naming_convention) || (scope == :group && params[:package_name_type] == :non_existing) status = :not_found end - status = :not_found if scope == :group && params[:package_name_type] == :non_existing + # Check the error message for :not_found + example_name = 'returning response status with error' if status == :not_found it_behaves_like example_name, status: status end @@ -858,6 +856,9 @@ RSpec.shared_examples 'handling different package names, visibilities and user r status = :not_found if scope == :group && params[:package_name_type] == :non_existing && params[:auth].present? + # Check the error message for :not_found + example_name = 'returning response status with error' if status == :not_found + it_behaves_like example_name, status: status end end diff --git a/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb index 7c20ea661b5..403344efe03 100644 --- a/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb @@ -36,18 +36,18 @@ RSpec.shared_examples 'accept package tags request' do |status:| end context 'with invalid package name' do - where(:package_name, :status) do - '%20' | :bad_request - nil | :not_found + where(:package_name, :status, :error) do + '%20' | :bad_request | '"Package Name" not given' + nil | :not_found | %r{\A(Packages|Project) not found\z} end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end end -RSpec.shared_examples 'accept create package tag request' do |user_type| +RSpec.shared_examples 'accept create package tag request' do |status:| using RSpec::Parameterized::TableSyntax context 'with valid package name' do @@ -92,45 +92,55 @@ RSpec.shared_examples 'accept create package tag request' do |user_type| expect(response.body).to be_empty end end + + context 'with ActiveRecord::RecordInvalid error' do + before do + allow_next_instance_of(Packages::Tag) do |tag| + allow(tag).to receive(:save!).and_raise(ActiveRecord::RecordInvalid) + end + end + + it_behaves_like 'returning response status with error', status: :bad_request, error: 'Record invalid' + end end context 'with invalid package name' do - where(:package_name, :status) do - 'unknown' | :not_found - '' | :not_found - '%20' | :bad_request + where(:package_name, :status, :error) do + 'unknown' | :not_found | %r{\A(Package|Project) not found\z} + '' | :not_found | '404 Not Found' + '%20' | :bad_request | '"Package Name" not given' end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end context 'with invalid tag name' do - where(:tag_name, :status) do - '' | :not_found - '%20' | :bad_request + where(:tag_name, :status, :error) do + '' | :not_found | '404 Not Found' + '%20' | :bad_request | '"Tag" not given' end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end context 'with invalid version' do - where(:version, :status) do - ' ' | :bad_request - '' | :bad_request - nil | :bad_request + where(:version, :status, :error) do + ' ' | :bad_request | '"Version" not given' + '' | :bad_request | '"Version" not given' + nil | :bad_request | '"Version" not given' end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end end -RSpec.shared_examples 'accept delete package tag request' do |user_type| +RSpec.shared_examples 'accept delete package tag request' do |status:| using RSpec::Parameterized::TableSyntax context 'with valid package name' do @@ -159,29 +169,39 @@ RSpec.shared_examples 'accept delete package tag request' do |user_type| it_behaves_like 'returning response status', :not_found end + + context 'with ActiveRecord::RecordInvalid error' do + before do + allow_next_instance_of(::Packages::RemoveTagService) do |service| + allow(service).to receive(:execute).and_raise(ActiveRecord::RecordInvalid) + end + end + + it_behaves_like 'returning response status with error', status: :bad_request, error: 'Record invalid' + end end context 'with invalid package name' do - where(:package_name, :status) do - 'unknown' | :not_found - '' | :not_found - '%20' | :bad_request + where(:package_name, :status, :error) do + 'unknown' | :not_found | %r{\A(Package tag|Project) not found\z} + '' | :not_found | '404 Not Found' + '%20' | :bad_request | '"Package Name" not given' end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end context 'with invalid tag name' do - where(:tag_name, :status) do - 'unknown' | :not_found - '' | :not_found - '%20' | :bad_request + where(:tag_name, :status, :error) do + 'unknown' | :not_found | %r{\A(Package tag|Project) not found\z} + '' | :not_found | '404 Not Found' + '%20' | :bad_request | '"Tag" not given' end with_them do - it_behaves_like 'returning response status', params[:status] + it_behaves_like 'returning response status with error', status: params[:status], error: params[:error] end end end diff --git a/spec/support/shared_examples/requests/response_status_with_error_shared_examples.rb b/spec/support/shared_examples/requests/response_status_with_error_shared_examples.rb new file mode 100644 index 00000000000..de012aebaad --- /dev/null +++ b/spec/support/shared_examples/requests/response_status_with_error_shared_examples.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'returning response status with error' do |status:, error: nil| + it "returns #{status} and error message" do + subject + + expect(response).to have_gitlab_http_status(status) + expect(json_response['error']).to be_present + expect(json_response['error']).to match(error) if error + end +end diff --git a/spec/support/shared_examples/services/auto_merge_shared_examples.rb b/spec/support/shared_examples/services/auto_merge_shared_examples.rb new file mode 100644 index 00000000000..b295b65fdd1 --- /dev/null +++ b/spec/support/shared_examples/services/auto_merge_shared_examples.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +RSpec.shared_context 'for auto_merge strategy context' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + + let(:mr_merge_if_green_enabled) do + create(:merge_request, + merge_when_pipeline_succeeds: true, + merge_user: user, + source_branch: 'master', target_branch: 'feature', + source_project: project, target_project: project, + state: 'opened') + end + + let(:pipeline) { create(:ci_pipeline, ref: mr_merge_if_green_enabled.source_branch, project: project) } + + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } + + before_all do + project.add_maintainer(user) + end + + before do + allow(MergeWorker).to receive(:with_status).and_return(MergeWorker) + end +end + +RSpec.shared_examples 'auto_merge service #execute' do + let(:merge_request) do + create(:merge_request, target_project: project, source_project: project, + source_branch: 'feature', target_branch: 'master') + end + + context 'when first time enabling' do + before do + allow(merge_request) + .to receive_messages(head_pipeline: pipeline, actual_head_pipeline: pipeline) + allow(MailScheduler::NotificationServiceWorker).to receive(:perform_async) + + service.execute(merge_request) + end + + it 'sets the params, merge_user, and flag' do + expect(merge_request).to be_valid + expect(merge_request.merge_when_pipeline_succeeds).to be_truthy + expect(merge_request.merge_params).to include 'commit_message' => 'Awesome message' + expect(merge_request.merge_user).to be user + expect(merge_request.auto_merge_strategy).to eq auto_merge_strategy + end + + it 'schedules a notification' do + expect(MailScheduler::NotificationServiceWorker).to have_received(:perform_async).with( + 'merge_when_pipeline_succeeds', merge_request, user).once + end + + it 'creates a system note' do + pipeline = build(:ci_pipeline) + allow(merge_request).to receive(:actual_head_pipeline) { pipeline } + + note = merge_request.notes.last + expect(note.note).to match expected_note + end + end + + context 'when already approved' do + let(:service) { described_class.new(project, user, should_remove_source_branch: true) } + let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) } + + before do + allow(mr_merge_if_green_enabled) + .to receive_messages(head_pipeline: pipeline, actual_head_pipeline: pipeline) + + allow(mr_merge_if_green_enabled).to receive(:mergeable?) + .and_return(true) + + allow(pipeline).to receive(:success?).and_return(true) + end + + it 'updates the merge params' do + expect(SystemNoteService).not_to receive(:merge_when_pipeline_succeeds) + expect(MailScheduler::NotificationServiceWorker).not_to receive(:perform_async).with( + 'merge_when_pipeline_succeeds', any_args) + + service.execute(mr_merge_if_green_enabled) + expect(mr_merge_if_green_enabled.merge_params).to have_key('should_remove_source_branch') + end + end +end + +RSpec.shared_examples 'auto_merge service #process' do + let(:merge_request_ref) { mr_merge_if_green_enabled.source_branch } + let(:merge_request_head) do + project.commit(mr_merge_if_green_enabled.source_branch).id + end + + context 'when triggered by pipeline with valid ref and sha' do + let(:triggering_pipeline) do + create(:ci_pipeline, project: project, ref: merge_request_ref, + sha: merge_request_head, status: 'success', + head_pipeline_of: mr_merge_if_green_enabled) + end + + it "merges all merge requests with merge when the pipeline succeeds enabled" do + allow(mr_merge_if_green_enabled) + .to receive_messages(head_pipeline: triggering_pipeline, actual_head_pipeline: triggering_pipeline) + + expect(MergeWorker).to receive(:perform_async) + service.process(mr_merge_if_green_enabled) + end + end + + context 'when triggered by an old pipeline' do + let(:old_pipeline) do + create(:ci_pipeline, project: project, ref: merge_request_ref, + sha: '1234abcdef', status: 'success') + end + + it 'does not merge request' do + expect(MergeWorker).not_to receive(:perform_async) + service.process(mr_merge_if_green_enabled) + end + end + + context 'when triggered by pipeline from a different branch' do + let(:unrelated_pipeline) do + create(:ci_pipeline, project: project, ref: 'feature', + sha: merge_request_head, status: 'success') + end + + it 'does not merge request' do + expect(MergeWorker).not_to receive(:perform_async) + service.process(mr_merge_if_green_enabled) + end + end + + context 'when pipeline is merge request pipeline' do + let(:pipeline) do + create(:ci_pipeline, :success, + source: :merge_request_event, + ref: mr_merge_if_green_enabled.merge_ref_path, + merge_request: mr_merge_if_green_enabled, + merge_requests_as_head_pipeline: [mr_merge_if_green_enabled]) + end + + it 'merges the associated merge request' do + allow(mr_merge_if_green_enabled) + .to receive_messages(head_pipeline: pipeline, actual_head_pipeline: pipeline) + + expect(MergeWorker).to receive(:perform_async) + service.process(mr_merge_if_green_enabled) + end + end +end + +RSpec.shared_examples 'auto_merge service #cancel' do + before do + service.cancel(mr_merge_if_green_enabled) + end + + it "resets all the pipeline succeeds params" do + expect(mr_merge_if_green_enabled.merge_when_pipeline_succeeds).to be_falsey + expect(mr_merge_if_green_enabled.merge_params).to eq({}) + expect(mr_merge_if_green_enabled.merge_user).to be nil + end + + it 'posts a system note' do + note = mr_merge_if_green_enabled.notes.last + expect(note.note).to include 'canceled the automatic merge' + end +end + +RSpec.shared_examples 'auto_merge service #abort' do + before do + service.abort(mr_merge_if_green_enabled, 'an error') + end + + it 'posts a system note' do + note = mr_merge_if_green_enabled.notes.last + expect(note.note).to include 'aborted the automatic merge' + end +end diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb index 162be24fe8f..9d016e4830e 100644 --- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb +++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb @@ -54,7 +54,7 @@ RSpec.shared_examples 'issues move service' do |group| context 'when moving to backlog' do let(:milestone) { create(:milestone, project: project) } - let!(:backlog) { create(:backlog_list, board: board1) } + let!(:backlog) { board1.lists.backlog.first } let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression], milestone: milestone) } let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: backlog.id } } diff --git a/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb index b9f28fab558..716ed482b4b 100644 --- a/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb +++ b/spec/support/shared_examples/services/boards/lists_list_service_shared_examples.rb @@ -2,7 +2,7 @@ RSpec.shared_examples 'lists list service' do context 'when the board has a backlog list' do - let!(:backlog_list) { create_backlog_list(board) } + let(:backlog_list) { board.lists.backlog.first } it 'does not create a backlog list' do expect { service.execute(board) }.not_to change { board.lists.count } @@ -34,6 +34,10 @@ RSpec.shared_examples 'lists list service' do end context 'when the board does not have a backlog list' do + before do + board.lists.backlog.delete_all + end + it 'creates a backlog list' do expect { service.execute(board) }.to change { board.lists.count }.by(1) end diff --git a/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb index 5336e0f4c2f..8a3ab07bbfe 100644 --- a/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb +++ b/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb @@ -30,6 +30,7 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type| context 'with a file generated by Gitlab CSV export' do let(:file) { fixture_file_upload('spec/fixtures/csv_gitlab_export.csv') } + let!(:test_milestone) { create(:milestone, project: project, title: 'v1.0') } it 'imports the CSV without errors' do expect(subject[:success]).to eq(4) diff --git a/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb b/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb new file mode 100644 index 00000000000..d4c00738bdb --- /dev/null +++ b/spec/support/shared_examples/views/nav_sidebar_shared_examples.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'has nav sidebar' do + it 'has collapsed nav sidebar on mobile' do + render + + expect(rendered).to have_selector('.nav-sidebar') + expect(rendered).not_to have_selector('.sidebar-collapsed-desktop') + expect(rendered).not_to have_selector('.sidebar-expanded-mobile') + end +end + +RSpec.shared_examples 'sidebar includes snowplow attributes' do |track_action, track_label, track_property| + specify do + stub_application_setting(snowplow_enabled: true) + + render + + expect(rendered) + .to have_css( + ".nav-sidebar[data-track-action=\"#{track_action}\"]" \ + "[data-track-label=\"#{track_label}\"][data-track-property=\"#{track_property}\"]" + ) + end +end diff --git a/spec/support/shared_examples/views/pipeline_status_changes_email.rb b/spec/support/shared_examples/views/pipeline_status_changes_email.rb index fe6cc5e03d2..a2db05c319e 100644 --- a/spec/support/shared_examples/views/pipeline_status_changes_email.rb +++ b/spec/support/shared_examples/views/pipeline_status_changes_email.rb @@ -25,6 +25,8 @@ RSpec.shared_examples 'pipeline status changes email' do end shared_examples_for 'renders the pipeline status changes email correctly' do + let(:pipeline_name_or_id) { pipeline.name || "##{pipeline.id}" } + context 'pipeline with user' do it 'renders the email correctly' do render @@ -33,11 +35,12 @@ RSpec.shared_examples 'pipeline status changes email' do expect(rendered).to have_content pipeline.project.name expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name - expect(rendered).to have_content "##{pipeline.id}" + expect(rendered).to have_content pipeline_name_or_id expect(rendered).to have_content pipeline.user.name if status == :failed expect(rendered).to have_content build.name + expect(rendered).to include("#{build.project.full_path}/-/jobs/#{build.id}") unless build.is_a?(Ci::Bridge) end end @@ -56,11 +59,12 @@ RSpec.shared_examples 'pipeline status changes email' do expect(rendered).to have_content pipeline.project.name expect(rendered).to have_content pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' ') expect(rendered).to have_content pipeline.commit.author_name - expect(rendered).to have_content "##{pipeline.id}" + expect(rendered).to have_content pipeline_name_or_id expect(rendered).to have_content "by API" if status == :failed expect(rendered).to have_content build.name + expect(rendered).to include("#{build.project.full_path}/-/jobs/#{build.id}") unless build.is_a?(Ci::Bridge) end end end diff --git a/spec/support/shared_examples/views/preferred_language.rb b/spec/support/shared_examples/views/preferred_language.rb new file mode 100644 index 00000000000..bd6c34bfcc7 --- /dev/null +++ b/spec/support/shared_examples/views/preferred_language.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a layout which reflects the preferred language' do + context 'when changing the a preferred language' do + before do + Gitlab::I18n.locale = :es + end + + after do + Gitlab::I18n.use_default_locale + end + + it 'renders the correct `lang` attribute in the html element' do + render + + expect(rendered).to have_css('html[lang=es]') + end + end +end |