summaryrefslogtreecommitdiff
path: root/spec/support
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
commita7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch)
tree7452bd5c3545c2fa67a28aa013835fb4fa071baf /spec/support
parentee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff)
downloadgitlab-ce-a7b3560714b4d9cc4ab32dffcd1f74a284b93580.tar.gz
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/cross_database_modification.rb9
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/flaky_tests.rb11
-rw-r--r--spec/support/gitlab_experiment.rb10
-rw-r--r--spec/support/graphql/arguments.rb1
-rw-r--r--spec/support/helpers/fake_blob_helpers.rb5
-rw-r--r--spec/support/helpers/features/invite_members_modal_helper.rb2
-rw-r--r--spec/support/helpers/features/iteration_helpers.rb6
-rw-r--r--spec/support/helpers/gitaly_setup.rb8
-rw-r--r--spec/support/helpers/import_spec_helper.rb2
-rw-r--r--spec/support/helpers/key_generator_helper.rb44
-rw-r--r--spec/support/helpers/login_helpers.rb2
-rw-r--r--spec/support/helpers/memory_usage_helper.rb37
-rw-r--r--spec/support/helpers/merge_request_diff_helpers.rb54
-rw-r--r--spec/support/helpers/note_interaction_helpers.rb4
-rw-r--r--spec/support/helpers/rack_attack_spec_helpers.rb4
-rw-r--r--spec/support/helpers/repo_helpers.rb2
-rw-r--r--spec/support/helpers/session_helpers.rb16
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb2
-rw-r--r--spec/support/helpers/test_env.rb11
-rw-r--r--spec/support/import_export/import_export.yml2
-rw-r--r--spec/support/matchers/event_store.rb12
-rw-r--r--spec/support/matchers/schema_matcher.rb34
-rw-r--r--spec/support/shared_contexts/container_repositories_shared_context.rb27
-rw-r--r--spec/support/shared_contexts/features/integrations/integrations_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb2
-rw-r--r--spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb6
-rw-r--r--spec/support/shared_contexts/graphql/requests/packages_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/lib/container_registry/client_shared_context.rb27
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb1
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb2
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb52
-rw-r--r--spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/features/variable_list_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/graphql/boards_shared_examples.rb58
-rw-r--r--spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb13
-rw-r--r--spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/integrations/integration_settings_form.rb47
-rw-r--r--spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb47
-rw-r--r--spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb10
-rw-r--r--spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb18
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb80
-rw-r--r--spec/support/shared_examples/models/note_access_check_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/namespaces/traversal_scope_examples.rb47
-rw-r--r--spec/support/shared_examples/path_extraction_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/policies/clusterable_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb85
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb93
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/workers/project_export_shared_examples.rb16
-rw-r--r--spec/support/stub_settings_source.rb11
70 files changed, 902 insertions, 265 deletions
diff --git a/spec/support/cross_database_modification.rb b/spec/support/cross_database_modification.rb
new file mode 100644
index 00000000000..e0d91001c03
--- /dev/null
+++ b/spec/support/cross_database_modification.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec.configure do |config|
+ config.after do |example|
+ [::ApplicationRecord, ::Ci::ApplicationRecord].each do |base_class|
+ base_class.gitlab_transactions_stack.clear if base_class.respond_to?(:gitlab_transactions_stack)
+ end
+ end
+end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index fb70f82ef87..e3a05f17593 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -73,6 +73,8 @@ module DbCleaner
end
end
+ Gitlab::Database::Partitioning.sync_partitions_ignore_db_error
+
puts "Databases re-creation done in #{Gitlab::Metrics::System.monotonic_time - start}"
end
diff --git a/spec/support/flaky_tests.rb b/spec/support/flaky_tests.rb
index 5ce55c47aab..4df0d23bfc3 100644
--- a/spec/support/flaky_tests.rb
+++ b/spec/support/flaky_tests.rb
@@ -4,14 +4,14 @@ return unless ENV['CI']
return if ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "false"
return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests')
+require_relative '../../tooling/rspec_flaky/config'
require_relative '../../tooling/rspec_flaky/report'
RSpec.configure do |config|
$flaky_test_example_ids = begin # rubocop:disable Style/GlobalVars
- raise "$SUITE_FLAKY_RSPEC_REPORT_PATH is empty." if ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'].to_s.empty?
- raise "#{ENV['SUITE_FLAKY_RSPEC_REPORT_PATH']} doesn't exist" unless File.exist?(ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'])
+ raise "#{RspecFlaky::Config.suite_flaky_examples_report_path} doesn't exist" unless File.exist?(RspecFlaky::Config.suite_flaky_examples_report_path)
- RspecFlaky::Report.load(ENV['SUITE_FLAKY_RSPEC_REPORT_PATH']).map { |_, flaky_test_data| flaky_test_data.to_h[:example_id] }
+ RspecFlaky::Report.load(RspecFlaky::Config.suite_flaky_examples_report_path).map { |_, flaky_test_data| flaky_test_data.to_h[:example_id] }
rescue => e # rubocop:disable Style/RescueStandardError
puts e
[]
@@ -29,8 +29,9 @@ RSpec.configure do |config|
end
config.after(:suite) do
- next unless ENV['SKIPPED_FLAKY_TESTS_REPORT_PATH']
+ next unless RspecFlaky::Config.skipped_flaky_tests_report_path
+ next if $skipped_flaky_tests_report.empty? # rubocop:disable Style/GlobalVars
- File.write(ENV['SKIPPED_FLAKY_TESTS_REPORT_PATH'], "#{$skipped_flaky_tests_report.join("\n")}\n") # rubocop:disable Style/GlobalVars
+ File.write(RspecFlaky::Config.skipped_flaky_tests_report_path, "#{ENV['CI_JOB_URL']}\n#{$skipped_flaky_tests_report.join("\n")}\n\n") # rubocop:disable Style/GlobalVars
end
end
diff --git a/spec/support/gitlab_experiment.rb b/spec/support/gitlab_experiment.rb
index 3d099dc689c..823aab0436e 100644
--- a/spec/support/gitlab_experiment.rb
+++ b/spec/support/gitlab_experiment.rb
@@ -10,6 +10,16 @@ RSpec.configure do |config|
# Disable all caching for experiments in tests.
config.before do
allow(Gitlab::Experiment::Configuration).to receive(:cache).and_return(nil)
+
+ # Disable all deprecation warnings in the test environment, which can be
+ # resolved one by one and tracked in:
+ #
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/350944
+ allow(Gitlab::Experiment::Configuration).to receive(:deprecator).and_wrap_original do |method, version|
+ method.call(version).tap do |deprecator|
+ deprecator.silenced = true
+ end
+ end
end
config.before(:each, :experiment) do
diff --git a/spec/support/graphql/arguments.rb b/spec/support/graphql/arguments.rb
index d8c334c2ca4..20e940030f8 100644
--- a/spec/support/graphql/arguments.rb
+++ b/spec/support/graphql/arguments.rb
@@ -41,6 +41,7 @@ module Graphql
when Hash then "{#{new(value)}}"
when Integer, Float, Symbol then value.to_s
when String then "\"#{value.gsub(/"/, '\\"')}\""
+ when Time, Date then "\"#{value.iso8601}\""
when nil then 'null'
when true then 'true'
when false then 'false'
diff --git a/spec/support/helpers/fake_blob_helpers.rb b/spec/support/helpers/fake_blob_helpers.rb
index 6c8866deac4..47fb9a345a3 100644
--- a/spec/support/helpers/fake_blob_helpers.rb
+++ b/spec/support/helpers/fake_blob_helpers.rb
@@ -4,13 +4,14 @@ module FakeBlobHelpers
class FakeBlob
include BlobLike
- attr_reader :path, :size, :data, :lfs_oid, :lfs_size
+ attr_reader :path, :size, :data, :lfs_oid, :lfs_size, :mode
- def initialize(path: 'file.txt', size: 1.kilobyte, data: 'foo', binary: false, lfs: nil)
+ def initialize(path: 'file.txt', size: 1.kilobyte, data: 'foo', binary: false, lfs: nil, mode: nil)
@path = path
@size = size
@data = data
@binary = binary
+ @mode = mode
@lfs_pointer = lfs.present?
if @lfs_pointer
diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb
index 11040562b49..2a4f78ca57f 100644
--- a/spec/support/helpers/features/invite_members_modal_helper.rb
+++ b/spec/support/helpers/features/invite_members_modal_helper.rb
@@ -8,7 +8,7 @@ module Spec
def invite_member(name, role: 'Guest', expires_at: nil)
click_on 'Invite members'
- page.within '[data-testid="invite-members-modal"]' do
+ page.within '[data-testid="invite-modal"]' do
find('[data-testid="members-token-select-input"]').set(name)
wait_for_requests
diff --git a/spec/support/helpers/features/iteration_helpers.rb b/spec/support/helpers/features/iteration_helpers.rb
new file mode 100644
index 00000000000..8e1d252f55f
--- /dev/null
+++ b/spec/support/helpers/features/iteration_helpers.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+module IterationHelpers
+ def iteration_period(iteration)
+ "#{iteration.start_date.to_s(:medium)} - #{iteration.due_date.to_s(:medium)}"
+ end
+end
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 905c439f4d9..a4ee618457d 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -120,7 +120,7 @@ module GitalySetup
end
def build_gitaly
- run_command(%w[make all git], env: env.merge('GIT_VERSION' => nil))
+ run_command(%w[make all WITH_BUNDLED_GIT=YesPlease], env: env.merge('GIT_VERSION' => nil))
end
def start_gitaly(toml = nil)
@@ -327,8 +327,8 @@ module GitalySetup
message += "\nCheck log/gitaly-test.log for errors.\n"
- unless ci?
- message += "\nIf binaries are missing, try running `make -C tmp/tests/gitaly build git.`\n"
+ unless ENV['CI']
+ message += "\nIf binaries are missing, try running `make -C tmp/tests/gitaly all WITH_BUNDLED_GIT=YesPlease`.\n"
message += "\nOtherwise, try running `rm -rf #{tmp_tests_gitaly_dir}`."
end
@@ -336,7 +336,7 @@ module GitalySetup
end
def git_binary
- File.join(tmp_tests_gitaly_dir, "_build", "deps", "git", "install", "bin", "git")
+ File.join(tmp_tests_gitaly_dir, "_build", "bin", "gitaly-git")
end
def gitaly_binary
diff --git a/spec/support/helpers/import_spec_helper.rb b/spec/support/helpers/import_spec_helper.rb
index d8fb2ba08af..26b78acbc44 100644
--- a/spec/support/helpers/import_spec_helper.rb
+++ b/spec/support/helpers/import_spec_helper.rb
@@ -25,7 +25,7 @@ module ImportSpecHelper
end
def stub_omniauth_provider(name)
- provider = OpenStruct.new(
+ provider = ActiveSupport::InheritableOptions.new(
name: name,
app_id: 'asd123',
app_secret: 'asd123'
diff --git a/spec/support/helpers/key_generator_helper.rb b/spec/support/helpers/key_generator_helper.rb
deleted file mode 100644
index 58bde80a31f..00000000000
--- a/spec/support/helpers/key_generator_helper.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module Spec
- module Support
- module Helpers
- class KeyGeneratorHelper
- # The components in a openssh .pub / known_host RSA public key.
- RSA_COMPONENTS = ['ssh-rsa', :e, :n].freeze
-
- attr_reader :size
-
- def initialize(size = 2048)
- @size = size
- end
-
- def generate
- key = OpenSSL::PKey::RSA.generate(size)
- components = RSA_COMPONENTS.map do |component|
- key.respond_to?(component) ? encode_mpi(key.public_send(component)) : component
- end
-
- # Ruby tries to be helpful and adds new lines every 60 bytes :(
- 'ssh-rsa ' + [pack_pubkey_components(components)].pack('m').delete("\n")
- end
-
- private
-
- # Encodes an openssh-mpi-encoded integer.
- def encode_mpi(n) # rubocop:disable Naming/UncommunicativeMethodParamName
- chars = []
- n = n.to_i
- chars << (n & 0xff) && n >>= 8 while n != 0
- chars << 0 if chars.empty? || chars.last >= 0x80
- chars.reverse.pack('C*')
- end
-
- # Packs string components into an openssh-encoded pubkey.
- def pack_pubkey_components(strings)
- (strings.flat_map { |s| [s.length].pack('N') }).zip(strings).join
- end
- end
- end
- end
-end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 4e0e8dd96ee..c0734bae375 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -178,7 +178,7 @@ module LoginHelpers
end
def mock_saml_config
- OpenStruct.new(name: 'saml', label: 'saml', args: {
+ ActiveSupport::InheritableOptions.new(name: 'saml', label: 'saml', args: {
assertion_consumer_service_url: 'https://localhost:3443/users/auth/saml/callback',
idp_cert_fingerprint: '26:43:2C:47:AF:F0:6B:D0:07:9C:AD:A3:74:FE:5D:94:5F:4E:9E:52',
idp_sso_target_url: 'https://idp.example.com/sso/saml',
diff --git a/spec/support/helpers/memory_usage_helper.rb b/spec/support/helpers/memory_usage_helper.rb
deleted file mode 100644
index 02d1935921f..00000000000
--- a/spec/support/helpers/memory_usage_helper.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-module MemoryUsageHelper
- extend ActiveSupport::Concern
-
- def gather_memory_data(csv_path)
- write_csv_entry(csv_path,
- {
- example_group_path: TestEnv.topmost_example_group[:location],
- example_group_description: TestEnv.topmost_example_group[:description],
- time: Time.current,
- job_name: ENV['CI_JOB_NAME']
- }.merge(get_memory_usage))
- end
-
- def write_csv_entry(path, entry)
- CSV.open(path, "a", headers: entry.keys, write_headers: !File.exist?(path)) do |file|
- file << entry.values
- end
- end
-
- def get_memory_usage
- output, status = Gitlab::Popen.popen(%w(free -m))
- abort "`free -m` return code is #{status}: #{output}" unless status == 0
-
- result = output.split("\n")[1].split(" ")[1..]
- attrs = %i(m_total m_used m_free m_shared m_buffers_cache m_available).freeze
-
- attrs.zip(result).to_h
- end
-
- included do |config|
- config.after(:all) do
- gather_memory_data(ENV['MEMORY_TEST_PATH']) if ENV['MEMORY_TEST_PATH']
- end
- end
-end
diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb
index 30afde7efed..7515c789add 100644
--- a/spec/support/helpers/merge_request_diff_helpers.rb
+++ b/spec/support/helpers/merge_request_diff_helpers.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: true
module MergeRequestDiffHelpers
+ PageEndReached = Class.new(StandardError)
+
def click_diff_line(line_holder, diff_side = nil)
line = get_line_components(line_holder, diff_side)
+ scroll_to_elements_bottom(line_holder)
line_holder.hover
line[:num].find('.js-add-diff-note-button').click
end
@@ -27,4 +30,55 @@ module MergeRequestDiffHelpers
line_holder.find('.diff-line-num', match: :first)
{ content: line_holder.all('.line_content')[side_index], num: line_holder.all('.diff-line-num')[side_index] }
end
+
+ def has_reached_page_end
+ evaluate_script("(window.innerHeight + window.scrollY) >= document.body.offsetHeight")
+ end
+
+ def scroll_to_elements_bottom(element)
+ evaluate_script("(function(el) {
+ window.scrollBy(0, el.getBoundingClientRect().bottom - window.innerHeight);
+ })(arguments[0]);", element.native)
+ end
+
+ # we're not using Capybara's .obscured here because it also checks if the element is clickable
+ def within_viewport?(element)
+ evaluate_script("(function(el) {
+ var rect = el.getBoundingClientRect();
+ return (
+ rect.bottom >= 0 &&
+ rect.right >= 0 &&
+ rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
+ rect.left <= (window.innerWidth || document.documentElement.clientWidth)
+ );
+ })(arguments[0]);", element.native)
+ end
+
+ def find_within_viewport(selector, **options)
+ begin
+ element = find(selector, **options, wait: 2)
+ rescue Capybara::ElementNotFound
+ return
+ end
+ return element if within_viewport?(element)
+
+ nil
+ end
+
+ def find_by_scrolling(selector, **options)
+ element = find_within_viewport(selector, **options)
+ return element if element
+
+ page.execute_script "window.scrollTo(0,0)"
+ until element
+
+ if has_reached_page_end
+ raise PageEndReached, "Failed to find any elements matching a selector '#{selector}' by scrolling. Page end reached."
+ end
+
+ page.execute_script "window.scrollBy(0,window.innerHeight/1.5)"
+ element = find_within_viewport(selector, **options)
+ end
+ element
+ end
end
diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb
index a4322618cd3..fa2705a64fa 100644
--- a/spec/support/helpers/note_interaction_helpers.rb
+++ b/spec/support/helpers/note_interaction_helpers.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
module NoteInteractionHelpers
+ include MergeRequestDiffHelpers
+
def open_more_actions_dropdown(note)
- note_element = find("#note_#{note.id}")
+ note_element = find_by_scrolling("#note_#{note.id}")
note_element.find('.more-actions-toggle').click
note_element.find('.more-actions .dropdown-menu li', match: :first)
diff --git a/spec/support/helpers/rack_attack_spec_helpers.rb b/spec/support/helpers/rack_attack_spec_helpers.rb
index d50a6382a40..c82a87dc58e 100644
--- a/spec/support/helpers/rack_attack_spec_helpers.rb
+++ b/spec/support/helpers/rack_attack_spec_helpers.rb
@@ -26,14 +26,14 @@ module RackAttackSpecHelpers
{ 'AUTHORIZATION' => "Basic #{encoded_login}" }
end
- def expect_rejection(&block)
+ def expect_rejection(name = nil, &block)
yield
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(response.headers.to_h).to include(
'RateLimit-Limit' => a_string_matching(/^\d+$/),
- 'RateLimit-Name' => a_string_matching(/^throttle_.*$/),
+ 'RateLimit-Name' => name || a_string_matching(/^throttle_.*$/),
'RateLimit-Observed' => a_string_matching(/^\d+$/),
'RateLimit-Remaining' => a_string_matching(/^\d+$/),
'Retry-After' => a_string_matching(/^\d+$/)
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index bbba58d60d6..f275be39dc4 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -129,7 +129,7 @@ eos
commit_message: 'Add new content')
Files::CreateService.new(
project,
- project.owner,
+ project.first_owner,
commit_message: commit_message,
start_branch: start_branch,
branch_name: branch_name,
diff --git a/spec/support/helpers/session_helpers.rb b/spec/support/helpers/session_helpers.rb
index 236585296e5..394a401afca 100644
--- a/spec/support/helpers/session_helpers.rb
+++ b/spec/support/helpers/session_helpers.rb
@@ -1,6 +1,22 @@
# frozen_string_literal: true
module SessionHelpers
+ # Stub a session in Redis, for use in request specs where we can't mock the session directly.
+ # This also needs the :clean_gitlab_redis_sessions tag on the spec.
+ def stub_session(session_hash)
+ unless RSpec.current_example.metadata[:clean_gitlab_redis_sessions]
+ raise 'Add :clean_gitlab_redis_sessions to your spec!'
+ end
+
+ session_id = Rack::Session::SessionId.new(SecureRandom.hex)
+
+ Gitlab::Redis::Sessions.with do |redis|
+ redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
+ end
+
+ cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ end
+
def expect_single_session_with_authenticated_ttl
expect_single_session_with_expiration(Settings.gitlab['session_expire_delay'] * 60)
end
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index c3459f7bc81..749554f7786 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -51,6 +51,8 @@ module StubGitlabCalls
allow(Gitlab.config.registry).to receive_messages(registry_settings)
allow(Auth::ContainerRegistryAuthenticationService)
.to receive(:full_access_token).and_return('token')
+ allow(Auth::ContainerRegistryAuthenticationService)
+ .to receive(:import_access_token).and_return('token')
end
def stub_container_registry_tags(repository: :any, tags: [], with_manifest: false)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 5c3ca92c4d0..18c25f4b770 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -371,17 +371,6 @@ module TestEnv
FileUtils.rm_rf(path)
end
- def current_example_group
- Thread.current[:current_example_group]
- end
-
- # looking for a top-level `describe`
- def topmost_example_group
- example_group = current_example_group
- example_group = example_group[:parent_example_group] until example_group[:parent_example_group].nil?
- example_group
- end
-
def seed_db
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import
end
diff --git a/spec/support/import_export/import_export.yml b/spec/support/import_export/import_export.yml
index 116bc8d0b9c..fa10e1335c5 100644
--- a/spec/support/import_export/import_export.yml
+++ b/spec/support/import_export/import_export.yml
@@ -28,4 +28,4 @@ excluded_attributes:
- :iid
project:
- :id
- - :created_at \ No newline at end of file
+ - :created_at
diff --git a/spec/support/matchers/event_store.rb b/spec/support/matchers/event_store.rb
new file mode 100644
index 00000000000..96a71ae3c22
--- /dev/null
+++ b/spec/support/matchers/event_store.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :event_type do |event_class|
+ match do |actual|
+ actual.instance_of?(event_class) &&
+ actual.data == @expected_data
+ end
+
+ chain :containing do |expected_data|
+ @expected_data = expected_data
+ end
+end
diff --git a/spec/support/matchers/schema_matcher.rb b/spec/support/matchers/schema_matcher.rb
index 5e08e96f4e1..d2f32b60464 100644
--- a/spec/support/matchers/schema_matcher.rb
+++ b/spec/support/matchers/schema_matcher.rb
@@ -36,12 +36,38 @@ end
RSpec::Matchers.define :match_response_schema do |schema, dir: nil, **options|
match do |response|
- schema_path = Pathname.new(SchemaPath.expand(schema, dir))
- validator = SchemaPath.validator(schema_path)
+ @schema_path = Pathname.new(SchemaPath.expand(schema, dir))
+ validator = SchemaPath.validator(@schema_path)
- data = Gitlab::Json.parse(response.body)
+ @data = Gitlab::Json.parse(response.body)
- validator.valid?(data)
+ @schema_errors = validator.validate(@data)
+ @schema_errors.none?
+ end
+
+ failure_message do |actual|
+ message = []
+
+ message << <<~MESSAGE
+ expected JSON response to match schema #{@schema_path.inspect}.
+
+ JSON input: #{Gitlab::Json.pretty_generate(@data).indent(2)}
+
+ Schema errors:
+ MESSAGE
+
+ @schema_errors.each do |error|
+ property_name, actual_value = error.values_at('data_pointer', 'data')
+ property_name = 'root' if property_name.empty?
+
+ message << <<~MESSAGE
+ Property: #{property_name}
+ Actual value: #{Gitlab::Json.pretty_generate(actual_value).indent(2)}
+ Error: #{JSONSchemer::Errors.pretty(error)}
+ MESSAGE
+ end
+
+ message.join("\n")
end
end
diff --git a/spec/support/shared_contexts/container_repositories_shared_context.rb b/spec/support/shared_contexts/container_repositories_shared_context.rb
new file mode 100644
index 00000000000..7f61631dce0
--- /dev/null
+++ b/spec/support/shared_contexts/container_repositories_shared_context.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'importable repositories' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:valid_container_repository) { create(:container_repository, project: project, created_at: 2.days.ago) }
+ let_it_be(:valid_container_repository2) { create(:container_repository, project: project, created_at: 1.year.ago) }
+ let_it_be(:importing_container_repository) { create(:container_repository, :importing, project: project, created_at: 2.days.ago) }
+ let_it_be(:new_container_repository) { create(:container_repository, project: project) }
+
+ let_it_be(:denied_group) { create(:group) }
+ let_it_be(:denied_project) { create(:project, group: denied_group) }
+ let_it_be(:denied_container_repository) { create(:container_repository, project: denied_project, created_at: 2.days.ago) }
+
+ before do
+ stub_application_setting(container_registry_import_created_before: 1.day.ago)
+ stub_feature_flags(
+ container_registry_phase_2_deny_list: false,
+ container_registry_migration_limit_gitlab_org: false
+ )
+
+ Feature::FlipperGate.create!(
+ feature_key: 'container_registry_phase_2_deny_list',
+ key: 'actors',
+ value: "Group:#{denied_group.id}"
+ )
+ end
+end
diff --git a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
index 3d2b0433b21..3ea6658c0c1 100644
--- a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
+++ b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
@@ -18,6 +18,8 @@ Integration.available_integration_names.each do |integration|
hash.merge!(k => 'https://example.atlassian.net/wiki')
elsif integration == 'datadog' && k == :datadog_site
hash.merge!(k => 'datadoghq.com')
+ elsif integration == 'datadog' && k == :datadog_tags
+ hash.merge!(k => 'key:value')
elsif integration == 'packagist' && k == :server
hash.merge!(k => 'https://packagist.example.com')
elsif k =~ /^(.*_url|url|webhook)/
diff --git a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
index 54bb9fd108e..fadd46a7e12 100644
--- a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
+++ b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_context 'project service Jira context' do
+RSpec.shared_context 'project integration Jira context' do
let(:url) { 'https://jira.example.com' }
let(:test_url) { 'https://jira.example.com/rest/api/2/serverInfo' }
diff --git a/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
index 6414a4d1eb3..bac7bd00f46 100644
--- a/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
+++ b/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_context 'project service activation' do
+RSpec.shared_context 'project integration activation' do
include_context 'integration activation'
let_it_be(:project) { create(:project) }
diff --git a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
index 6b15eadc1c1..3479dac0077 100644
--- a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
RSpec.shared_context 'GroupProjectsFinder context' do
- let_it_be(:group) { create(:group) }
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:current_user) { create(:user) }
let(:params) { {} }
@@ -16,6 +17,9 @@ RSpec.shared_context 'GroupProjectsFinder context' do
let_it_be(:shared_project_3) { create(:project, :internal, path: '5', name: 'c') }
let_it_be(:subgroup_project) { create(:project, :public, path: '6', group: subgroup, name: 'b') }
let_it_be(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup, name: 'a') }
+ let_it_be(:root_group_public_project) { create(:project, :public, path: '8', group: root_group, name: 'root-public-project') }
+ let_it_be(:root_group_private_project) { create(:project, :private, path: '9', group: root_group, name: 'root-private-project') }
+ let_it_be(:root_group_private_project_2) { create(:project, :private, path: '10', group: root_group, name: 'root-private-project-2') }
before do
shared_project_1.project_group_links.create!(group_access: Gitlab::Access::MAINTAINER, group: group)
diff --git a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
index 9ac3d4a04f9..13e7ecf2669 100644
--- a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
+++ b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
@@ -11,7 +11,7 @@ RSpec.shared_context 'package details setup' do
let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:dependency_links) { all_graphql_fields_for('PackageDependencyLink') }
let(:pipelines) { all_graphql_fields_for('Pipeline', max_depth: 1) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:first_file) { package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
diff --git a/spec/support/shared_contexts/lib/container_registry/client_shared_context.rb b/spec/support/shared_contexts/lib/container_registry/client_shared_context.rb
new file mode 100644
index 00000000000..a87a3247b95
--- /dev/null
+++ b/spec/support/shared_contexts/lib/container_registry/client_shared_context.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'container registry client' do
+ let(:token) { '12345' }
+ let(:options) { { token: token } }
+ let(:registry_api_url) { 'http://container-registry' }
+ let(:client) { described_class.new(registry_api_url, options) }
+ let(:push_blob_headers) do
+ {
+ 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
+ 'Authorization' => "bearer #{token}",
+ 'Content-Type' => 'application/octet-stream',
+ 'User-Agent' => "GitLab/#{Gitlab::VERSION}"
+ }
+ end
+
+ let(:headers_with_accept_types) do
+ {
+ 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
+ 'Authorization' => "bearer #{token}",
+ 'User-Agent' => "GitLab/#{Gitlab::VERSION}"
+ }
+ end
+
+ let(:expected_faraday_headers) { { user_agent: "GitLab/#{Gitlab::VERSION}" } }
+ let(:expected_faraday_request_options) { Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS }
+end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 27967850389..576a8aa44fa 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -22,6 +22,7 @@ RSpec.shared_context 'project navbar structure' do
nav_sub_items: [
_('Activity'),
_('Labels'),
+ _('Planning hierarchy'),
_('Members')
]
},
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 0dfd76de79c..76db2bd82f1 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -47,6 +47,7 @@ RSpec.shared_context 'GroupPolicy context' do
create_custom_emoji
create_package
create_package_settings
+ read_cluster
]
end
@@ -54,7 +55,7 @@ RSpec.shared_context 'GroupPolicy context' do
%i[
destroy_package
create_projects
- read_cluster create_cluster update_cluster admin_cluster add_cluster
+ create_cluster update_cluster admin_cluster add_cluster
]
end
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index c39252cef13..3641edc845a 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -17,7 +17,7 @@ RSpec.shared_context 'ProjectPolicy context' do
%i[
award_emoji create_issue create_merge_request_in create_note
create_project read_issue_board read_issue read_issue_iid read_issue_link
- read_label read_issue_board_list read_milestone read_note read_project
+ read_label read_planning_hierarchy read_issue_board_list read_milestone read_note read_project
read_project_for_iids read_project_member read_release read_snippet
read_wiki upload_file
]
diff --git a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
index 21be989d697..e26b8cd8b37 100644
--- a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
+++ b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
@@ -3,7 +3,7 @@
RSpec.shared_context 'container repository delete tags service shared context' do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :private) }
- let_it_be(:repository) { create(:container_repository, :root, project: project) }
+ let_it_be_with_reload(:repository) { create(:container_repository, :root, project: project) }
let(:params) { { tags: tags } }
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 62b35923bcd..5ed8dc7ce98 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -205,10 +205,30 @@ RSpec.shared_examples 'handle uploads' do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
- it "responds with status 200" do
- show_upload
+ context "enforce_auth_checks_on_uploads feature flag" do
+ context "with flag enabled" do
+ before do
+ stub_feature_flags(enforce_auth_checks_on_uploads: true)
+ end
- expect(response).to have_gitlab_http_status(:ok)
+ it "responds with status 302" do
+ show_upload
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ end
+ end
+
+ context "with flag disabled" do
+ before do
+ stub_feature_flags(enforce_auth_checks_on_uploads: false)
+ end
+
+ it "responds with status 200" do
+ show_upload
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
end
@@ -276,10 +296,30 @@ RSpec.shared_examples 'handle uploads' do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
- it "responds with status 200" do
- show_upload
+ context "enforce_auth_checks_on_uploads feature flag" do
+ context "with flag enabled" do
+ before do
+ stub_feature_flags(enforce_auth_checks_on_uploads: true)
+ end
+
+ it "responds with status 404" do
+ show_upload
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context "with flag disabled" do
+ before do
+ stub_feature_flags(enforce_auth_checks_on_uploads: false)
+ end
+
+ it "responds with status 200" do
+ show_upload
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
index e0a032b1a43..8a07e52019c 100644
--- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
+++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
@@ -2,7 +2,7 @@
RSpec.shared_examples 'comment on merge request file' do
it 'adds a comment' do
- click_diff_line(find("[id='#{sample_commit.line_code}']"))
+ click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
page.within('.js-discussion-note-form') do
fill_in(:note_note, with: 'Line is wrong')
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 1c816ee4b0a..456175e7113 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -62,7 +62,7 @@ RSpec.shared_examples 'a creatable merge request' do
end
it 'updates the branches when selecting a new target project', :js do
- target_project_member = target_project.owner
+ target_project_member = target_project.first_owner
::Branches::CreateService.new(target_project, target_project_member)
.execute('a-brand-new-branch-to-test', 'master')
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
index a9dac7a391f..281a70e46c4 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -54,7 +54,10 @@ RSpec.shared_examples 'labels sidebar widget' do
end
fill_in 'Search', with: 'Devel'
- sleep 1
+ expect(page).to have_css('.labels-fetch-loading')
+ wait_for_all_requests
+
+ expect(page).to have_css('[data-testid="dropdown-content"] .gl-new-dropdown-item')
expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1)
find_field('Search').native.send_keys(:enter)
diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb
index 52451839281..c63faace6b2 100644
--- a/spec/support/shared_examples/features/variable_list_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb
@@ -166,7 +166,7 @@ RSpec.shared_examples 'variable list' do
wait_for_requests
expect(find('.flash-container')).to be_present
- expect(find('.flash-text').text).to have_content('Variables key (key) has already been taken')
+ expect(find('[data-testid="alert-danger"]').text).to have_content('Variables key (key) has already been taken')
end
it 'prevents a variable to be added if no values are provided when a variable is set to masked' do
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 1a981f42086..2285d9a17e2 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
@@ -85,7 +85,9 @@ RSpec.shared_examples 'User previews wiki changes' do
end
it 'renders content with CommonMark' do
- fill_in :wiki_content, with: "1. one\n - sublist\n"
+ # using two `\n` ensures we're sublist to it's own line due
+ # to list auto-continue
+ fill_in :wiki_content, with: "1. one\n\n - sublist\n"
click_on "Preview"
# the above generates two separate lists (not embedded) in CommonMark
diff --git a/spec/support/shared_examples/graphql/boards_shared_examples.rb b/spec/support/shared_examples/graphql/boards_shared_examples.rb
new file mode 100644
index 00000000000..e8a4c17fb92
--- /dev/null
+++ b/spec/support/shared_examples/graphql/boards_shared_examples.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'querying a GraphQL type recent boards' do
+ describe 'Get list of recently visited boards' do
+ let(:boards_data) { graphql_data[board_type]['recentIssueBoards']['nodes'] }
+
+ context 'when the request is correct' do
+ before do
+ visit_board
+ parent.add_reporter(user)
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns recent boards for user successfully' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_errors).to be_nil
+ expect(boards_data.size).to eq(1)
+ expect(boards_data[0]['name']).to eq(board.name)
+ end
+ end
+
+ context 'when requests has errors' do
+ context 'when there are no recently visited boards' do
+ it 'returns empty result' do
+ post_graphql(query, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_errors).to be_nil
+ expect(boards_data).to be_empty
+ end
+ end
+ end
+ end
+
+ def query(query_params: {}, full_path: parent.full_path)
+ board_nodes = <<~NODE
+ nodes {
+ name
+ }
+ NODE
+
+ graphql_query_for(
+ board_type.to_sym,
+ { full_path: full_path },
+ query_graphql_field(:recent_issue_boards, query_params, board_nodes)
+ )
+ end
+
+ def visit_board
+ if board_type == 'group'
+ create(:board_group_recent_visit, group: parent, board: board, user: user)
+ else
+ create(:board_project_recent_visit, project: parent, board: board, user: user)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
index 011a2157f24..b17e59f0797 100644
--- a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb
@@ -16,17 +16,4 @@ RSpec.shared_examples 'a mutation which can mutate a spammable' do
subject
end
end
-
- describe "#spam_action_response_fields" do
- it 'resolves with spam action fields' do
- subject
-
- # NOTE: We do not need to assert on the specific values of spam action fields here, we only need
- # to verify that #spam_action_response_fields was invoked and that the fields are present in the
- # response. The specific behavior of #spam_action_response_fields is covered in the
- # HasSpamActionResponseFields unit tests.
- expect(mutation_response.keys)
- .to include('spam', 'spamLogId', 'needsCaptchaResponse', 'captchaSiteKey')
- end
- end
end
diff --git a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
index 2bb3d807aa7..14b2663a72c 100644
--- a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples_for 'graphql mutations security ci configuration' do
ServiceResponse.success(payload: { branch: branch, success_path: success_path })
end
- let(:error) { "An error occured!" }
+ let(:error) { "An error occurred!" }
let(:service_error_response) do
ServiceResponse.error(message: error)
diff --git a/spec/support/shared_examples/integrations/integration_settings_form.rb b/spec/support/shared_examples/integrations/integration_settings_form.rb
new file mode 100644
index 00000000000..d0bb40e43ee
--- /dev/null
+++ b/spec/support/shared_examples/integrations/integration_settings_form.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'integration settings form' do
+ include IntegrationsHelper
+ # Note: these specs don't validate channel fields
+ # which are present on a few integrations
+ it 'displays all the integrations' do
+ aggregate_failures do
+ integrations.each do |integration|
+ navigate_to_integration(integration)
+
+ page.within('form.integration-settings-form') do
+ expect(page).to have_field('Active', type: 'checkbox', wait: 0),
+ "#{integration.title} active field not present"
+
+ fields = parse_json(fields_for_integration(integration))
+ fields.each do |field|
+ field_name = field[:name]
+ expect(page).to have_field(field[:title], wait: 0),
+ "#{integration.title} field #{field_name} not present"
+ end
+
+ events = parse_json(trigger_events_for_integration(integration))
+ events.each do |trigger|
+ # normalizing the title because capybara location is case sensitive
+ title = normalize_title trigger[:title], integration
+
+ expect(page).to have_field(title, type: 'checkbox', wait: 0),
+ "#{integration.title} field #{title} checkbox not present"
+ end
+ end
+ end
+ end
+ end
+
+ private
+
+ def normalize_title(title, integration)
+ return 'Merge request' if integration.is_a?(Integrations::Jira) && title == 'merge_request'
+
+ title.titlecase
+ end
+
+ def parse_json(json)
+ Gitlab::Json.parse(json, symbolize_names: true)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb
index 5baa6478225..fdca326dbea 100644
--- a/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
RSpec.shared_examples 'tracks assignment and records the subject' do |experiment, subject_type|
+ before do
+ stub_experiments(experiment => true)
+ end
+
it 'tracks the assignment', :experiment do
expect(experiment(experiment))
.to track(:assignment)
@@ -11,9 +15,7 @@ RSpec.shared_examples 'tracks assignment and records the subject' do |experiment
end
it 'records the subject' do
- stub_experiments(experiment => :candidate)
-
- expect(Experiment).to receive(:add_subject).with(experiment.to_s, variant: :experimental, subject: subject)
+ expect(Experiment).to receive(:add_subject).with(experiment.to_s, variant: anything, subject: subject)
action
end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb
new file mode 100644
index 00000000000..6221366ab51
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'a request from an extension' do |event|
+ before do
+ stub_application_setting(usage_ping_enabled: true)
+ end
+
+ def count_unique(date_from:, date_to:)
+ Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
+ end
+
+ def track_action(params)
+ described_class.track_api_request_when_trackable(**params)
+ end
+
+ it 'tracks when the user agent is matching' do
+ aggregate_failures do
+ expect(track_action(user: user1, **user_agent)).to be_truthy
+ expect(track_action(user: user1, **user_agent)).to be_truthy
+ expect(track_action(user: user2, **user_agent)).to be_truthy
+
+ expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(2)
+ end
+ end
+
+ it 'does not track when the user agent is not matching' do
+ aggregate_failures do
+ user_agent = { user_agent: 'normal_user_agent' }
+
+ expect(track_action(user: user1, **user_agent)).to be_falsey
+ expect(track_action(user: user1, **user_agent)).to be_falsey
+ expect(track_action(user: user2, **user_agent)).to be_falsey
+
+ expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(0)
+ end
+ end
+
+ it 'does not track if user agent is not present' do
+ expect(track_action(user: nil, user_agent: nil)).to be_nil
+ end
+
+ it 'does not track if user is not present' do
+ expect(track_action(user: nil, **user_agent)).to be_nil
+ end
+end
diff --git a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
index d3fd28727b5..b4c438771ce 100644
--- a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
+++ b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.shared_examples 'ZenTao menu with CE version' do
let(:project) { create(:project, has_external_issue_tracker: true) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
let(:zentao_integration) { create(:zentao_integration, project: project) }
diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
index 42eec74e64f..5f59d43ad19 100644
--- a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
+++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb
@@ -7,6 +7,12 @@ RSpec.shared_examples 'it has loose foreign keys' do
let(:fully_qualified_table_name) { "#{connection.current_schema}.#{table_name}" }
let(:deleted_records) { LooseForeignKeys::DeletedRecord.where(fully_qualified_table_name: fully_qualified_table_name) }
+ around do |example|
+ LooseForeignKeys::DeletedRecord.using_connection(connection) do
+ example.run
+ end
+ end
+
it 'has at least one loose foreign key definition' do
definitions = Gitlab::Database::LooseForeignKeys.definitions_by_table[table_name]
expect(definitions.size).to be > 0
@@ -69,7 +75,9 @@ RSpec.shared_examples 'cleanup by a loose foreign key' do
expect(find_model).to be_present
- LooseForeignKeys::ProcessDeletedRecordsService.new(connection: model.connection).execute
+ LooseForeignKeys::DeletedRecord.using_connection(parent.connection) do
+ LooseForeignKeys::ProcessDeletedRecordsService.new(connection: parent.connection).execute
+ end
if foreign_key_definition.on_delete.eql?(:async_delete)
expect(find_model).not_to be_present
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 d823e7ac221..8ff30021d6e 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
@@ -178,4 +178,22 @@ RSpec.shared_examples 'StageEventModel' do
end
end
end
+
+ describe '#total_time' do
+ it 'calcualtes total time from the start_event_timestamp and end_event_timestamp columns' do
+ model = described_class.new(start_event_timestamp: Time.new(2022, 1, 1, 12, 5, 0), end_event_timestamp: Time.new(2022, 1, 1, 12, 6, 30))
+
+ expect(model.total_time).to eq(90)
+ end
+
+ context 'when total time is calculated in SQL as an extra column' do
+ it 'returns the SQL calculated time' do
+ create(stage_event_factory) # rubocop:disable Rails/SaveBang
+
+ model = described_class.select('*, 5 AS total_time').first
+
+ expect(model.total_time).to eq(5)
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 5b4b8c8fcc1..f7e09cfca62 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -301,8 +301,9 @@ RSpec.shared_examples_for "member creation" do
end
context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do
+ let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source }
+
it 'creates a member_task with the correct attributes', :aggregate_failures do
- task_project = source.is_a?(Group) ? create(:project, group: source) : source
described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute
member = source.members.last
@@ -310,6 +311,43 @@ RSpec.shared_examples_for "member creation" do
expect(member.tasks_to_be_done).to match_array([:ci, :code])
expect(member.member_task.project).to eq(task_project)
end
+
+ context 'with an already existing member' do
+ before do
+ source.add_user(user, :developer)
+ end
+
+ it 'does not update tasks to be done if tasks already exist', :aggregate_failures do
+ member = source.members.find_by(user_id: user.id)
+ create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci))
+
+ expect do
+ described_class.new(source,
+ user,
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id).execute
+ end.not_to change(MemberTask, :count)
+
+ member.reset
+ expect(member.tasks_to_be_done).to match_array([:code, :ci])
+ expect(member.member_task.project).to eq(task_project)
+ end
+
+ it 'adds tasks to be done if they do not exist', :aggregate_failures do
+ expect do
+ described_class.new(source,
+ user,
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id).execute
+ end.to change(MemberTask, :count).by(1)
+
+ member = source.members.find_by(user_id: user.id)
+ expect(member.tasks_to_be_done).to match_array([:issues])
+ expect(member.member_task.project).to eq(task_project)
+ end
+ end
end
end
end
@@ -393,14 +431,52 @@ RSpec.shared_examples_for "bulk member creation" do
end
context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do
+ let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source }
+
it 'creates a member_task with the correct attributes', :aggregate_failures do
- task_project = source.is_a?(Group) ? create(:project, group: source) : source
members = described_class.add_users(source, [user1], :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id)
member = members.last
expect(member.tasks_to_be_done).to match_array([:ci, :code])
expect(member.member_task.project).to eq(task_project)
end
+
+ context 'with an already existing member' do
+ before do
+ source.add_user(user1, :developer)
+ end
+
+ it 'does not update tasks to be done if tasks already exist', :aggregate_failures do
+ member = source.members.find_by(user_id: user1.id)
+ create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci))
+
+ expect do
+ described_class.add_users(source,
+ [user1.id],
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id)
+ end.not_to change(MemberTask, :count)
+
+ member.reset
+ expect(member.tasks_to_be_done).to match_array([:code, :ci])
+ expect(member.member_task.project).to eq(task_project)
+ end
+
+ it 'adds tasks to be done if they do not exist', :aggregate_failures do
+ expect do
+ described_class.add_users(source,
+ [user1.id],
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id)
+ end.to change(MemberTask, :count).by(1)
+
+ member = source.members.find_by(user_id: user1.id)
+ expect(member.tasks_to_be_done).to match_array([:issues])
+ expect(member.member_task.project).to eq(task_project)
+ end
+ end
end
end
end
diff --git a/spec/support/shared_examples/models/note_access_check_shared_examples.rb b/spec/support/shared_examples/models/note_access_check_shared_examples.rb
index 44edafe9091..0c9992b832f 100644
--- a/spec/support/shared_examples/models/note_access_check_shared_examples.rb
+++ b/spec/support/shared_examples/models/note_access_check_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'users with note access' do
it 'returns true' do
users.each do |user|
- expect(note.system_note_with_references_visible_for?(user)).to be_truthy
+ expect(note.system_note_visible_for?(user)).to be_truthy
expect(note.readable_by?(user)).to be_truthy
end
end
@@ -12,7 +12,7 @@ end
RSpec.shared_examples 'users without note access' do
it 'returns false' do
users.each do |user|
- expect(note.system_note_with_references_visible_for?(user)).to be_falsy
+ expect(note.system_note_visible_for?(user)).to be_falsy
expect(note.readable_by?(user)).to be_falsy
end
end
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index 3f8c3b8960b..6b0ae589efb 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -235,18 +235,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it 'does not return them' do
expect(subject.to_a).not_to include(package_file_pending_destruction)
end
-
- context 'with packages_installable_package_files disabled' do
- before do
- stub_feature_flags(packages_installable_package_files: false)
- end
-
- it 'returns them' do
- subject
-
- expect(subject.to_a).to include(package_file_pending_destruction)
- end
- end
end
end
end
diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
index 06326ffac97..ad0bbc0aeff 100644
--- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
@@ -115,14 +115,14 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
expect(ProjectStatistics)
.not_to receive(:increment_statistic)
- expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true)
+ expect(Projects::DestroyService.new(project, project.first_owner).execute).to eq(true)
end
it 'does not schedule a namespace statistics worker' do
expect(Namespaces::ScheduleAggregationWorker)
.not_to receive(:perform_async)
- expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true)
+ expect(Projects::DestroyService.new(project, project.first_owner).execute).to eq(true)
end
end
end
diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
index b43b7946e69..bcb5464ed5b 100644
--- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
@@ -299,4 +299,51 @@ RSpec.shared_examples 'namespace traversal scopes' do
include_examples '.self_and_descendant_ids'
end
end
+
+ shared_examples '.self_and_hierarchy' do
+ let(:base_scope) { Group.where(id: base_groups) }
+
+ subject { base_scope.self_and_hierarchy }
+
+ context 'with ancestors only' do
+ let(:base_groups) { [group_1, group_2] }
+
+ it { is_expected.to match_array(groups) }
+ end
+
+ context 'with descendants only' do
+ let(:base_groups) { [deep_nested_group_1, deep_nested_group_2] }
+
+ it { is_expected.to match_array(groups) }
+ end
+
+ context 'nodes with both ancestors and descendants' do
+ let(:base_groups) { [nested_group_1, nested_group_2] }
+
+ it { is_expected.to match_array(groups) }
+ end
+
+ context 'with duplicate base groups' do
+ let(:base_groups) { [nested_group_1, nested_group_1] }
+
+ it { is_expected.to contain_exactly(group_1, nested_group_1, deep_nested_group_1) }
+ end
+ end
+
+ describe '.self_and_hierarchy' do
+ it_behaves_like '.self_and_hierarchy'
+
+ context "use_traversal_ids_for_self_and_hierarchy_scopes feature flag is false" do
+ before do
+ stub_feature_flags(use_traversal_ids_for_self_and_hierarchy_scopes: false)
+ end
+
+ it_behaves_like '.self_and_hierarchy'
+
+ it 'make recursive queries' do
+ base_groups = Group.where(id: nested_group_1)
+ expect { base_groups.self_and_hierarchy.load }.to make_queries_matching(/WITH RECURSIVE/)
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/path_extraction_shared_examples.rb b/spec/support/shared_examples/path_extraction_shared_examples.rb
index 39c7c1f2a94..d76348aa26a 100644
--- a/spec/support/shared_examples/path_extraction_shared_examples.rb
+++ b/spec/support/shared_examples/path_extraction_shared_examples.rb
@@ -40,12 +40,13 @@ RSpec.shared_examples 'assigns ref vars' do
end
context 'path contains space' do
- let(:params) { { path: 'with space', ref: '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' } }
+ let(:ref) { '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' }
+ let(:path) { 'with space' }
it 'is not converted to %20 in @path' do
assign_ref_vars
- expect(@path).to eq(params[:path])
+ expect(@path).to eq(path)
end
end
diff --git a/spec/support/shared_examples/policies/clusterable_shared_examples.rb b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
index b96aa71acbe..faf283f9059 100644
--- a/spec/support/shared_examples/policies/clusterable_shared_examples.rb
+++ b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
@@ -6,12 +6,24 @@ RSpec.shared_examples 'clusterable policies' do
subject { described_class.new(current_user, clusterable) }
+ context 'with a reporter' do
+ before do
+ clusterable.add_reporter(current_user)
+ end
+
+ it { expect_disallowed(:read_cluster) }
+ it { expect_disallowed(:add_cluster) }
+ it { expect_disallowed(:create_cluster) }
+ it { expect_disallowed(:update_cluster) }
+ it { expect_disallowed(:admin_cluster) }
+ end
+
context 'with a developer' do
before do
clusterable.add_developer(current_user)
end
- it { expect_disallowed(:read_cluster) }
+ it { expect_allowed(:read_cluster) }
it { expect_disallowed(:add_cluster) }
it { expect_disallowed(:create_cluster) }
it { expect_disallowed(:update_cluster) }
diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
index 052fd0622d0..f414500f202 100644
--- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
@@ -66,7 +66,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
it 'shows the help state when icon is clicked' do
page.within '.time-tracking-component-wrap' do
- find('.help-button').click
+ find('[data-testid="helpButton"]').click
expect(page).to have_content 'Track time with quick actions'
expect(page).to have_content 'Learn more'
end
@@ -92,8 +92,8 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
it 'hides the help state when close icon is clicked' do
page.within '.time-tracking-component-wrap' do
- find('.help-button').click
- find('.close-help-button').click
+ find('[data-testid="helpButton"]').click
+ find('[data-testid="closeHelpButton"]').click
expect(page).not_to have_content 'Track time with quick actions'
expect(page).not_to have_content 'Learn more'
@@ -102,7 +102,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
it 'displays the correct help url' do
page.within '.time-tracking-component-wrap' do
- find('.help-button').click
+ find('[data-testid="helpButton"]').click
expect(find_link('Learn more')[:href]).to have_content('/help/user/project/time_tracking.md')
end
diff --git a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
index 28decb4011d..2258bdd2c79 100644
--- a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
@@ -73,6 +73,16 @@ RSpec.shared_examples 'rebase quick action' do
expect(page).to have_content 'This merge request cannot be rebased while there are conflicts.'
end
end
+
+ context 'when the merge request branch is protected from force push' do
+ let!(:protected_branch) { create(:protected_branch, project: project, name: merge_request.source_branch, allow_force_push: false) }
+
+ it 'does not rebase the MR' do
+ add_note("/rebase")
+
+ expect(page).to have_content 'This merge request branch is protected from force push.'
+ end
+ end
end
context 'when the current user cannot rebase the MR' do
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index 8bffd1f71e9..a42a1fda62e 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -10,6 +10,8 @@ RSpec.shared_examples 'when the snippet is not found' do
end
RSpec.shared_examples 'snippet edit usage data counters' do
+ include SessionHelpers
+
context 'when user is sessionless' do
it 'does not track usage data actions' do
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
@@ -20,14 +22,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do
context 'when user is not sessionless', :clean_gitlab_redis_sessions do
before do
- session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
- session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] }
-
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]])
end
it 'tracks usage data actions', :clean_gitlab_redis_sessions do
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
index 882c79cb03f..127b1a6d4c4 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
@@ -3,11 +3,11 @@
RSpec.shared_examples 'group and project packages query' do
include GraphqlHelpers
- let_it_be(:versionaless_package) { create(:maven_package, project: project1, version: nil) }
- let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
- let_it_be(:package) { create(:npm_package, project: project1, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
- let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
- let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
+ let_it_be(:versionless_package) { create(:maven_package, project: project1, version: nil) }
+ let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'bab', version: '6.0.0', created_at: 1.day.ago) }
+ let_it_be(:npm_package) { create(:npm_package, project: project1, name: 'cab', version: '7.0.0', created_at: 4.days.ago) }
+ let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'dab', version: '4.0.0', created_at: 3.days.ago) }
+ let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'aab', version: '5.0.0', created_at: 2.days.ago) }
let_it_be(:composer_metadatum) do
create(:composer_metadatum, package: composer_package,
target_sha: 'afdeh',
@@ -21,11 +21,11 @@ RSpec.shared_examples 'group and project packages query' do
let(:fields) do
<<~QUERY
- count
- nodes {
- #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
- metadata { #{query_graphql_fragment('ComposerMetadata')} }
- }
+ count
+ nodes {
+ #{all_graphql_fields_for('packages'.classify, excluded: ['project'])}
+ metadata { #{query_graphql_fragment('ComposerMetadata')} }
+ }
QUERY
end
@@ -47,7 +47,7 @@ RSpec.shared_examples 'group and project packages query' do
it 'returns packages successfully' do
expect(package_names).to contain_exactly(
- package.name,
+ npm_package.name,
maven_package.name,
debian_package.name,
composer_package.name
@@ -88,7 +88,23 @@ RSpec.shared_examples 'group and project packages query' do
end
describe 'sorting and pagination' do
- let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
+ let_it_be(:packages_order_map) do
+ {
+ TYPE_ASC: [maven_package, npm_package, composer_package, debian_package],
+ TYPE_DESC: [debian_package, composer_package, npm_package, maven_package],
+
+ NAME_ASC: [debian_package, maven_package, npm_package, composer_package],
+ NAME_DESC: [composer_package, npm_package, maven_package, debian_package],
+
+ VERSION_ASC: [composer_package, debian_package, maven_package, npm_package],
+ VERSION_DESC: [npm_package, maven_package, debian_package, composer_package],
+
+ CREATED_ASC: [npm_package, composer_package, debian_package, maven_package],
+ CREATED_DESC: [maven_package, debian_package, composer_package, npm_package]
+ }
+ end
+
+ let(:expected_packages) { sorted_packages.map { |package| global_id_of(package) } }
let(:data_path) { [resource_type, :packages] }
@@ -96,22 +112,14 @@ RSpec.shared_examples 'group and project packages query' do
resource.add_reporter(current_user)
end
- [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
+ [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC, :CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
context "#{order}" do
- it_behaves_like 'sorted paginated query' do
- let(:sort_param) { order }
- let(:first_param) { 4 }
- let(:all_records) { ascending_packages }
- end
- end
- end
+ let(:sorted_packages) { packages_order_map.fetch(order) }
- [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
- context "#{order}" do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { order }
let(:first_param) { 4 }
- let(:all_records) { ascending_packages.reverse }
+ let(:all_records) { expected_packages }
end
end
end
@@ -180,7 +188,7 @@ RSpec.shared_examples 'group and project packages query' do
context 'include_versionless' do
let(:params) { { include_versionless: true } }
- it { is_expected.to include({ "name" => versionaless_package.name }) }
+ it { is_expected.to include({ "name" => versionless_package.name }) }
end
end
end
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
index 9385706d991..ab93f54111b 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
@@ -49,17 +49,5 @@ RSpec.shared_examples 'a package with files' do
expect(response_package_file_ids).not_to include(package_file_pending_destruction.to_global_id.to_s)
end
-
- context 'with packages_installable_package_files disabled' do
- before(:context) do
- stub_feature_flags(packages_installable_package_files: false)
- end
-
- it 'returns them' do
- expect(package.reload.package_files).to include(package_file_pending_destruction)
-
- expect(response_package_file_ids).to include(package_file_pending_destruction.to_global_id.to_s)
- end
- end
end
end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index 0434d0beb7e..2a157f6e855 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -190,7 +190,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
if parent_type == 'projects'
context 'by a project owner' do
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
it 'sets the creation time on the new note' do
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index b294467d482..c6c6c44dce8 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -580,3 +580,88 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
end
end
end
+
+# Requires let variables:
+# * throttle_setting_prefix: "throttle_authenticated", "throttle_unauthenticated"
+RSpec.shared_examples 'rate-limited frontend API requests' do
+ let(:requests_per_period) { 1 }
+ let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) }
+ let(:csrf_session) { { _csrf_token: csrf_token } }
+ let(:personal_access_token) { nil }
+
+ let(:api_path) { '/projects' }
+
+ # These don't actually exist, so a 404 is the expected response.
+ let(:files_api_path) { '/projects/1/repository/files/ref/path' }
+ let(:packages_api_path) { '/projects/1/packages/foo' }
+ let(:deprecated_api_path) { '/groups/1?with_projects=true' }
+
+ def get_api(path: api_path, csrf: false)
+ headers = csrf ? { 'X-CSRF-Token' => csrf_token } : nil
+ get api(path, personal_access_token: personal_access_token), headers: headers
+ end
+
+ def expect_not_found(&block)
+ yield
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ before do
+ stub_application_setting(
+ "#{throttle_setting_prefix}_enabled" => true,
+ "#{throttle_setting_prefix}_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_api_enabled" => true,
+ "#{throttle_setting_prefix}_api_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_web_enabled" => true,
+ "#{throttle_setting_prefix}_web_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_files_api_enabled" => true,
+ "#{throttle_setting_prefix}_packages_api_enabled" => true,
+ "#{throttle_setting_prefix}_deprecated_api_enabled" => true
+ )
+
+ stub_session(csrf_session)
+ end
+
+ context 'with a CSRF token' do
+ it 'uses the rate limit for web requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: files_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: packages_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: deprecated_api_path }
+
+ # API rate limit is not triggered yet
+ expect_ok { get_api }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+
+ context 'without a CSRF session' do
+ let(:csrf_session) { nil }
+
+ it 'always uses the rate limit for API requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+ end
+ end
+ end
+
+ context 'without a CSRF token' do
+ it 'uses the rate limit for API requests' do
+ requests_per_period.times { get_api }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+
+ # Web and custom API rate limits are not triggered yet
+ expect_ok { get_api csrf: true }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+ 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 d9b837258ce..a46c2f0ac5c 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
@@ -140,19 +140,6 @@ RSpec.shared_examples 'issues move service' do |group|
expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0)
end
- if group
- context 'when on a group board' do
- it 'sends the board_group_id parameter' do
- params.merge!(move_after_id: issue1.id, move_before_id: issue2.id)
-
- match_params = { move_between_ids: [issue1.id, issue2.id], board_group_id: parent.id }
- expect(Issues::UpdateService).to receive(:new).with(project: issue.project, current_user: user, params: match_params).and_return(double(execute: build(:issue)))
-
- described_class.new(parent, user, params).execute(issue)
- end
- end
- end
-
def reorder_issues(params, issues: [])
issues.each do |issue|
issue.move_to_end && issue.save!
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 87bf134eeb8..c808b9a5318 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -71,7 +71,6 @@ end
RSpec.shared_examples 'an accessible' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
- stub_feature_flags(container_registry_cdn_redirect: false)
end
let(:access) do
@@ -164,10 +163,9 @@ RSpec.shared_examples 'a container registry auth service' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
- stub_feature_flags(container_registry_cdn_redirect: false)
end
- describe '#full_access_token' do
+ describe '.full_access_token' do
let_it_be(:project) { create(:project) }
let(:token) { described_class.full_access_token(project.full_path) }
@@ -181,7 +179,26 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'not a container repository factory'
end
- describe '#pull_access_token' do
+ describe '.import_access_token' do
+ let(:access) do
+ [{ 'type' => 'registry',
+ 'name' => 'import',
+ 'actions' => ['*'] }]
+ end
+
+ let(:token) { described_class.import_access_token }
+
+ subject { { token: token } }
+
+ it_behaves_like 'a valid token'
+ it_behaves_like 'not a container repository factory'
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+ end
+
+ describe '.pull_access_token' do
let_it_be(:project) { create(:project) }
let(:token) { described_class.pull_access_token(project.full_path) }
@@ -1126,4 +1143,72 @@ RSpec.shared_examples 'a container registry auth service' do
end
end
end
+
+ context 'when importing' do
+ let_it_be(:container_repository) { create(:container_repository, :root, :importing) }
+ let_it_be(:current_project) { container_repository.project }
+ let_it_be(:current_user) { create(:user) }
+
+ before do
+ current_project.add_developer(current_user)
+ end
+
+ shared_examples 'containing the import error' do
+ it 'includes a helpful error message' do
+ expect(subject[:errors].first).to include(message: /Your repository is currently being migrated/)
+ end
+ end
+
+ context 'push request' do
+ let(:current_params) do
+ { scopes: ["repository:#{container_repository.path}:push"] }
+ end
+
+ it_behaves_like 'a forbidden' do
+ it_behaves_like 'containing the import error'
+ end
+ end
+
+ context 'delete request' do
+ let(:current_params) do
+ { scopes: ["repository:#{container_repository.path}:delete"] }
+ end
+
+ it_behaves_like 'a forbidden' do
+ it_behaves_like 'containing the import error'
+ end
+ end
+
+ context '* request' do
+ let(:current_params) do
+ { scopes: ["repository:#{container_repository.path}:*"] }
+ end
+
+ it_behaves_like 'a forbidden' do
+ it_behaves_like 'containing the import error'
+ end
+ end
+
+ context 'pull request' do
+ let(:current_params) do
+ { scopes: ["repository:#{container_repository.path}:pull"] }
+ end
+
+ let(:project) { current_project }
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'mixed request' do
+ let(:current_params) do
+ { scopes: ["repository:#{container_repository.path}:pull,push"] }
+ end
+
+ let(:project) { current_project }
+
+ it_behaves_like 'a forbidden' do
+ it_behaves_like 'containing the import error'
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index 36b0acf5a51..cc26cf87322 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -28,28 +28,15 @@ end
#
# include_examples 'not an incident issue'
RSpec.shared_examples 'not an incident issue' do
- let(:label_properties) { attributes_for(:label, :incident) }
-
it 'has not incident as issue type' do
expect(issue.issue_type).not_to eq('incident')
expect(issue.work_item_type.base_type).not_to eq('incident')
end
-
- it_behaves_like 'does not have incident label'
-end
-
-RSpec.shared_examples 'does not have incident label' do
- let(:label_properties) { attributes_for(:label, :incident) }
-
- it 'has not an incident label' do
- expect(issue.labels).not_to include(have_attributes(label_properties))
- end
end
# This shared example is to test the execution of incident management label services
# For example:
# - IncidentManagement::CreateIncidentSlaExceededLabelService
-# - IncidentManagement::CreateIncidentLabelService
# It doesn't require any defined variables
diff --git a/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb
new file mode 100644
index 00000000000..661a96266f1
--- /dev/null
+++ b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'renders registration features prompt' do |disabled_field|
+ it 'renders a placeholder input with registration features message', :aggregate_failures do
+ render
+
+ if disabled_field
+ expect(rendered).to have_field(disabled_field, disabled: true)
+ end
+
+ expect(rendered).to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: s_('RegistrationFeatures|use this feature') })
+ expect(rendered).to have_link(s_('RegistrationFeatures|Registration Features Program'))
+ end
+end
+
+RSpec.shared_examples 'does not render registration features prompt' do |disabled_field|
+ it 'does not render a placeholder input with registration features message', :aggregate_failures do
+ render
+
+ if disabled_field
+ expect(rendered).not_to have_field(disabled_field, disabled: true)
+ end
+
+ expect(rendered).not_to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: s_('RegistrationFeatures|use this feature') })
+ expect(rendered).not_to have_link(s_('RegistrationFeatures|Registration Features Program'))
+ end
+end
diff --git a/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb
index 0d3e158d358..7fdf049a823 100644
--- a/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'it runs background migration jobs' do |tracking_database, metric_name|
+RSpec.shared_examples 'it runs background migration jobs' do |tracking_database|
describe 'defining the job attributes' do
it 'defines the data_consistency as always' do
expect(described_class.get_data_consistency).to eq(:always)
@@ -33,16 +33,6 @@ RSpec.shared_examples 'it runs background migration jobs' do |tracking_database,
end
end
- describe '.unhealthy_metric_name' do
- it 'does not raise an error' do
- expect { described_class.unhealthy_metric_name }.not_to raise_error
- end
-
- it 'overrides the method to return the unhealthy metric name' do
- expect(described_class.unhealthy_metric_name).to eq(metric_name)
- end
- end
-
describe '.minimum_interval' do
it 'returns 2 minutes' do
expect(described_class.minimum_interval).to eq(2.minutes.to_i)
@@ -189,11 +179,11 @@ RSpec.shared_examples 'it runs background migration jobs' do |tracking_database,
end
it 'increments the unhealthy counter' do
- counter = Gitlab::Metrics.counter(metric_name, 'msg')
+ counter = Gitlab::Metrics.counter(:background_migration_database_health_reschedules, 'msg')
expect(described_class).to receive(:perform_in)
- expect { worker.perform('Foo', [10, 20]) }.to change { counter.get }.by(1)
+ expect { worker.perform('Foo', [10, 20]) }.to change { counter.get(db_config_name: tracking_database) }.by(1)
end
context 'when lease_attempts is 0' do
diff --git a/spec/support/shared_examples/workers/project_export_shared_examples.rb b/spec/support/shared_examples/workers/project_export_shared_examples.rb
index a9bcc3f4f7c..175ef9bd012 100644
--- a/spec/support/shared_examples/workers/project_export_shared_examples.rb
+++ b/spec/support/shared_examples/workers/project_export_shared_examples.rb
@@ -53,6 +53,10 @@ RSpec.shared_examples 'export worker' do
it 'does not raise an exception when strategy is invalid' do
expect(::Projects::ImportExport::ExportService).not_to receive(:new)
+ expect_next_instance_of(ProjectExportJob) do |job|
+ expect(job).to receive(:finish)
+ end
+
expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error
end
@@ -63,6 +67,18 @@ RSpec.shared_examples 'export worker' do
it 'does not raise error when user cannot be found' do
expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error
end
+
+ it 'fails the export job status' do
+ expect_next_instance_of(::Projects::ImportExport::ExportService) do |service|
+ expect(service).to receive(:execute).and_raise(Gitlab::ImportExport::Error)
+ end
+
+ expect_next_instance_of(ProjectExportJob) do |job|
+ expect(job).to receive(:fail_op)
+ end
+
+ expect { subject.perform(user.id, project.id, {}) }.to raise_error(Gitlab::ImportExport::Error)
+ end
end
end
diff --git a/spec/support/stub_settings_source.rb b/spec/support/stub_settings_source.rb
new file mode 100644
index 00000000000..c0e4e468b90
--- /dev/null
+++ b/spec/support/stub_settings_source.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+RSpec.configure do |config|
+ config.around(:each, stub_settings_source: true) do |example|
+ original_instance = ::Settings.instance_variable_get(:@instance)
+
+ example.run
+
+ ::Settings.instance_variable_set(:@instance, original_instance)
+ end
+end