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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-14 18:09:43 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-14 18:09:43 +0300
commit7a33080fff9a735cbe77968d67b13ffa92c0ffae (patch)
tree71a880649c8d3e551ec6bd94e93d08a9a9b5bde7 /spec
parent9223573b85bcfdd21953f52e0d2c5cb587e366a1 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/projects/settings/branch_rules_settings_spec.rb9
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb9
-rw-r--r--spec/features/users/signup_spec.rb116
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/packages/pipelines.json6
-rw-r--r--spec/graphql/mutations/users/set_namespace_commit_email_spec.rb75
-rw-r--r--spec/graphql/resolvers/blobs_resolver_spec.rb89
-rw-r--r--spec/graphql/resolvers/last_commit_resolver_spec.rb24
-rw-r--r--spec/lib/extracts_ref_spec.rb59
-rw-r--r--spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb9
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb50
-rw-r--r--spec/policies/user_policy_spec.rb4
-rw-r--r--spec/presenters/blob_presenter_spec.rb26
-rw-r--r--spec/presenters/tree_entry_presenter_spec.rb16
-rw-r--r--spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb106
-rw-r--r--spec/requests/api/project_packages_spec.rb243
-rw-r--r--spec/services/users/set_namespace_commit_email_service_spec.rb195
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb7
17 files changed, 961 insertions, 82 deletions
diff --git a/spec/features/projects/settings/branch_rules_settings_spec.rb b/spec/features/projects/settings/branch_rules_settings_spec.rb
index a6ecfa2d231..5ef80521401 100644
--- a/spec/features/projects/settings/branch_rules_settings_spec.rb
+++ b/spec/features/projects/settings/branch_rules_settings_spec.rb
@@ -45,14 +45,5 @@ RSpec.describe 'Projects > Settings > Repository > Branch rules settings', featu
expect(page).to have_content('Branch rules')
end
end
-
- context 'branch_rules feature flag disabled' do
- it 'does not render branch rules content' do
- stub_feature_flags(branch_rules: false)
- request
-
- expect(page).to have_gitlab_http_status(:not_found)
- end
- end
end
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index a801f633882..2439e624dd6 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe 'Projects > Settings > Repository settings', feature_category: :g
let(:role) { :developer }
before do
- stub_feature_flags(branch_rules: false)
stub_feature_flags(mirror_only_branches_match_regex: false)
project.add_role(user, role)
sign_in(user)
@@ -43,15 +42,7 @@ RSpec.describe 'Projects > Settings > Repository settings', feature_category: :g
end
context 'Branch rules', :js do
- context 'branch_rules feature flag disabled', :js do
- it 'does not render branch rules settings' do
- visit project_settings_repository_path(project)
- expect(page).not_to have_content('Branch rules')
- end
- end
-
it 'renders branch rules settings' do
- stub_feature_flags(branch_rules: true)
visit project_settings_repository_path(project)
expect(page).to have_content('Branch rules')
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index ccecd8bfcad..850dd0bbc5d 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -3,10 +3,8 @@
require 'spec_helper'
RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
- flag_values = [true, false]
- flag_values.each do |val|
+ shared_examples 'signup validation' do
before do
- stub_feature_flags(restyle_login_page: val)
visit new_user_registration_path
end
@@ -42,6 +40,18 @@ RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
end
end
end
+
+ include_examples 'signup validation'
+
+ # Inline `shared_example 'signup validation'` again after feature flag
+ # `restyle_login_page` was removed.
+ context 'with feature flag restyle_login_page disabled' do
+ before do
+ stub_feature_flags(restyle_login_page: false)
+ end
+
+ include_examples 'signup validation'
+ end
end
RSpec.describe 'Signup', :js, feature_category: :user_profile do
@@ -49,25 +59,32 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
let(:new_user) { build_stubbed(:user) }
- def fill_in_signup_form
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
- fill_in 'new_user_first_name', with: new_user.first_name
- fill_in 'new_user_last_name', with: new_user.last_name
- fill_in 'new_user_password', with: new_user.password
+ let(:terms_text) do
+ <<~TEXT.squish
+ By clicking Register or registering through a third party you accept the
+ Terms of Use and acknowledge the Privacy Policy and Cookie Policy
+ TEXT
end
- def confirm_email
- new_user_token = User.find_by_email(new_user.email).confirmation_token
+ shared_examples 'signup process' do
+ def fill_in_signup_form
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ fill_in 'new_user_password', with: new_user.password
- visit user_confirmation_path(confirmation_token: new_user_token)
- end
+ wait_for_all_requests
+ end
+
+ def confirm_email
+ new_user_token = User.find_by_email(new_user.email).confirmation_token
+
+ visit user_confirmation_path(confirmation_token: new_user_token)
+ end
- flag_values = [true, false]
- flag_values.each do |val|
before do
stub_feature_flags(arkose_labs_signup_challenge: false)
- stub_feature_flags(restyle_login_page: val)
stub_application_setting(require_admin_approval_after_user_signup: false)
end
@@ -162,7 +179,8 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
expect(page).to have_content("Invalid input, please avoid emojis")
end
- it 'shows a pending message if the username availability is being fetched' do
+ it 'shows a pending message if the username availability is being fetched',
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/31484' do
fill_in 'new_user_username', with: 'new-user'
expect(find('.username > .validation-pending')).not_to have_css '.hide'
@@ -263,7 +281,10 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
expect { click_button 'Register' }.to change { User.count }.by(1)
expect(page).to have_current_path new_user_session_path, ignore_query: true
- expect(page).to have_content("You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator")
+ expect(page).to have_content(<<~TEXT.squish)
+ You have signed up successfully. However, we could not sign you in
+ because your account is awaiting approval from your GitLab administrator
+ TEXT
end
end
end
@@ -305,13 +326,26 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
it 'renders text that the user confirms terms by signing in' do
visit new_user_registration_path
- expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)
+ expect(page).to have_content(terms_text)
fill_in_signup_form
click_button 'Register'
- expect(page).to have_current_path users_sign_up_welcome_path, ignore_query: true
+ expect(page).to have_current_path(users_sign_up_welcome_path), ignore_query: true
+ visit new_project_path
+
+ select 'Software Developer', from: 'user_role'
+ click_button 'Get started!'
+
+ created_user = User.find_by_username(new_user.username)
+
+ expect(created_user.software_developer_role?).to be_truthy
+ expect(created_user.setup_for_company).to be_nil
+ expect(page).to have_current_path(new_project_path)
end
+
+ it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
+ it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
end
context 'when reCAPTCHA and invisible captcha are enabled' do
@@ -337,7 +371,8 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content(_('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'))
- expect(page).to have_content("Minimum length is #{Gitlab::CurrentSettings.minimum_password_length} characters")
+ expect(page).to have_content(
+ "Minimum length is #{Gitlab::CurrentSettings.minimum_password_length} characters")
end
end
@@ -357,7 +392,6 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
visit new_user_registration_path
fill_in_signup_form
- wait_for_all_requests
click_button 'Register'
@@ -393,34 +427,22 @@ RSpec.describe 'Signup', :js, feature_category: :user_profile do
end
end
- context 'when terms are enforced' do
- before do
- enforce_terms
- end
-
- it 'renders text that the user confirms terms by signing in' do
- visit new_user_registration_path
-
- expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)
-
- fill_in_signup_form
- click_button 'Register'
-
- visit new_project_path
-
- expect(page).to have_current_path(users_sign_up_welcome_path)
+ include_examples 'signup process'
- select 'Software Developer', from: 'user_role'
- click_button 'Get started!'
-
- created_user = User.find_by_username(new_user.username)
+ # Inline `shared_example 'signup process'` again after feature flag
+ # `restyle_login_page` was removed.
+ context 'with feature flag restyle_login_page disabled' do
+ let(:terms_text) do
+ <<~TEXT.squish
+ By clicking Register, I agree that I have read and accepted the Terms of
+ Use and Privacy Policy
+ TEXT
+ end
- expect(created_user.software_developer_role?).to be_truthy
- expect(created_user.setup_for_company).to be_nil
- expect(page).to have_current_path(new_project_path)
+ before do
+ stub_feature_flags(restyle_login_page: false)
end
- it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
- it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
+ include_examples 'signup process'
end
end
diff --git a/spec/fixtures/api/schemas/public_api/v4/packages/pipelines.json b/spec/fixtures/api/schemas/public_api/v4/packages/pipelines.json
new file mode 100644
index 00000000000..3432503212a
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/packages/pipelines.json
@@ -0,0 +1,6 @@
+{
+ "type": "array",
+ "items": {
+ "$ref": "../pipeline.json"
+ }
+}
diff --git a/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb b/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb
new file mode 100644
index 00000000000..6d8e15ac791
--- /dev/null
+++ b/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Users::SetNamespaceCommitEmail, feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:email) { create(:email, user: current_user) }
+ let(:input) { {} }
+ let(:namespace_id) { group.to_global_id }
+ let(:email_id) { email.to_global_id }
+
+ shared_examples 'success' do
+ it 'creates namespace commit email with correct values' do
+ expect(resolve_mutation[:namespace_commit_email])
+ .to have_attributes({ namespace_id: namespace_id.model_id.to_i, email_id: email_id.model_id.to_i })
+ end
+ end
+
+ describe '#resolve' do
+ subject(:resolve_mutation) do
+ described_class.new(object: nil, context: { current_user: current_user }, field: nil).resolve(
+ namespace_id: namespace_id,
+ email_id: email_id
+ )
+ end
+
+ context 'when current_user does not have permission' do
+ it 'raises an error' do
+ expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ .with_message(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
+ end
+ end
+
+ context 'when the user has permission' do
+ before do
+ group.add_reporter(current_user)
+ end
+
+ context 'when the email does not belong to the target user' do
+ let(:email_id) { create(:email).to_global_id }
+
+ it 'returns the validation error' do
+ expect(resolve_mutation[:errors]).to contain_exactly("Email must be provided.")
+ end
+ end
+
+ context 'when namespace is a group' do
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a user' do
+ let(:namespace_id) { current_user.namespace.to_global_id }
+
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a project' do
+ let_it_be(:project) { create(:project) }
+
+ let(:namespace_id) { project.project_namespace.to_global_id }
+
+ before do
+ project.add_reporter(current_user)
+ end
+
+ it_behaves_like 'success'
+ end
+ end
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_namespace) }
+end
diff --git a/spec/graphql/resolvers/blobs_resolver_spec.rb b/spec/graphql/resolvers/blobs_resolver_spec.rb
index 26eb6dc0abe..0d725f00d43 100644
--- a/spec/graphql/resolvers/blobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/blobs_resolver_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe Resolvers::BlobsResolver do
+RSpec.describe Resolvers::BlobsResolver, feature_category: :source_code_management do
include GraphqlHelpers
+ include RepoHelpers
describe '.resolver_complexity' do
it 'adds one per path being resolved' do
@@ -59,15 +60,89 @@ RSpec.describe Resolvers::BlobsResolver do
end
end
- context 'specifying a different ref' do
+ context 'when specifying a branch ref' do
let(:ref) { 'add-pdf-file' }
+ let(:args) { { paths: paths, ref: ref, ref_type: ref_type } }
let(:paths) { ['files/pdf/test.pdf', 'README.md'] }
- it 'returns the specified blobs for that ref' do
- is_expected.to contain_exactly(
- have_attributes(path: 'files/pdf/test.pdf'),
- have_attributes(path: 'README.md')
- )
+ context 'and no ref_type is specified' do
+ let(:ref_type) { nil }
+
+ it 'returns the specified blobs for that ref' do
+ is_expected.to contain_exactly(
+ have_attributes(path: 'files/pdf/test.pdf'),
+ have_attributes(path: 'README.md')
+ )
+ end
+
+ context 'and a tag with the same name exists' do
+ let(:ref) { SecureRandom.uuid }
+
+ before do
+ project.repository.create_branch(ref)
+ create_file_in_repo(project, ref, ref, 'branch_file', 'Test file', commit_message: 'Add new content')
+ project.repository.add_tag(project.owner, sample_commit.id, ref)
+ end
+
+ it 'returns the specified blobs for the tag' do
+ is_expected.to contain_exactly(
+ have_attributes(path: 'README.md')
+ )
+ end
+ end
+ end
+
+ context 'and ref_type is for branches' do
+ let(:args) { { paths: paths, ref: ref, ref_type: 'heads' } }
+
+ it 'returns nothing' do
+ is_expected.to contain_exactly(
+ have_attributes(path: 'files/pdf/test.pdf'),
+ have_attributes(path: 'README.md')
+ )
+ end
+ end
+
+ context 'and ref_type is for tags' do
+ let(:args) { { paths: paths, ref: ref, ref_type: 'tags' } }
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ context 'when specifying a tag ref' do
+ let(:ref) { 'v1.0.0' }
+
+ let(:args) { { paths: paths, ref: ref, ref_type: ref_type } }
+
+ context 'and no ref_type is specified' do
+ let(:ref_type) { nil }
+
+ it 'returns the specified blobs for that ref' do
+ is_expected.to contain_exactly(
+ have_attributes(path: 'README.md')
+ )
+ end
+ end
+
+ context 'and ref_type is for tags' do
+ let(:ref_type) { 'tags' }
+
+ it 'returns the specified blobs for that ref' do
+ is_expected.to contain_exactly(
+ have_attributes(path: 'README.md')
+ )
+ end
+ end
+
+ context 'and ref_type is for branches' do
+ let(:ref_type) { 'heads' }
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
end
end
diff --git a/spec/graphql/resolvers/last_commit_resolver_spec.rb b/spec/graphql/resolvers/last_commit_resolver_spec.rb
index 5ac6ad59864..82bbdd4487c 100644
--- a/spec/graphql/resolvers/last_commit_resolver_spec.rb
+++ b/spec/graphql/resolvers/last_commit_resolver_spec.rb
@@ -61,5 +61,29 @@ RSpec.describe Resolvers::LastCommitResolver do
expect(commit).to be_nil
end
end
+
+ context 'when the ref is ambiguous' do
+ let(:ambiguous_ref) { 'v1.0.0' }
+
+ before do
+ project.repository.create_branch(ambiguous_ref)
+ end
+
+ context 'when tree is for a tag' do
+ let(:tree) { repository.tree(ambiguous_ref, ref_type: 'tags') }
+
+ it 'resolves commit' do
+ expect(commit.id).to eq(repository.find_tag(ambiguous_ref).dereferenced_target.id)
+ end
+ end
+
+ context 'when tree is for a branch' do
+ let(:tree) { repository.tree(ambiguous_ref, ref_type: 'heads') }
+
+ it 'resolves commit' do
+ expect(commit.id).to eq(repository.find_branch(ambiguous_ref).target)
+ end
+ end
+ end
end
end
diff --git a/spec/lib/extracts_ref_spec.rb b/spec/lib/extracts_ref_spec.rb
index 93a09bf5a0a..ac403ad642a 100644
--- a/spec/lib/extracts_ref_spec.rb
+++ b/spec/lib/extracts_ref_spec.rb
@@ -57,5 +57,64 @@ RSpec.describe ExtractsRef do
end
end
+ describe '#ref_type' do
+ let(:params) { ActionController::Parameters.new(ref_type: 'heads') }
+
+ it 'delegates to .ref_type' do
+ expect(described_class).to receive(:ref_type).with('heads')
+ ref_type
+ end
+ end
+
+ describe '.ref_type' do
+ subject { described_class.ref_type(ref_type) }
+
+ context 'when ref_type is nil' do
+ let(:ref_type) { nil }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'when ref_type is heads' do
+ let(:ref_type) { 'heads' }
+
+ it { is_expected.to eq('heads') }
+ end
+
+ context 'when ref_type is tags' do
+ let(:ref_type) { 'tags' }
+
+ it { is_expected.to eq('tags') }
+ end
+
+ context 'when ref_type is invalid' do
+ let(:ref_type) { 'invalid' }
+
+ it { is_expected.to eq(nil) }
+ end
+ end
+
+ describe '.qualify_ref' do
+ subject { described_class.qualify_ref(ref, ref_type) }
+
+ context 'when ref_type is nil' do
+ let(:ref_type) { nil }
+
+ it { is_expected.to eq(ref) }
+ end
+
+ context 'when ref_type valid' do
+ let(:ref_type) { 'heads' }
+
+ it { is_expected.to eq("refs/#{ref_type}/#{ref}") }
+ end
+
+ context 'when ref_type is invalid' do
+ let(:ref_type) { 'invalid' }
+
+ it { is_expected.to eq(ref) }
+ end
+ end
+
it_behaves_like 'extracts refs'
end
diff --git a/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb b/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb
index dc62fcb4478..7cee65c13f7 100644
--- a/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb
+++ b/spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb
@@ -14,6 +14,10 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do
expect(subject.available_for_type?(Ci::Build.all)).to be_truthy
end
+ it 'returns true for Packages::BuildInfo' do
+ expect(subject.available_for_type?(Packages::BuildInfo.all)).to be_truthy
+ end
+
it 'return false for other types of relations' do
expect(subject.available_for_type?(User.all)).to be_falsey
end
@@ -56,6 +60,7 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do
it 'return false for other types of relations' do
expect(subject.available?(cursor_based_request_context, User.all)).to be_falsey
expect(subject.available?(cursor_based_request_context, Ci::Build.all)).to be_falsey
+ expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_falsey
end
end
@@ -70,6 +75,10 @@ RSpec.describe Gitlab::Pagination::CursorBasedKeyset do
it 'returns true for AuditEvent' do
expect(subject.available?(cursor_based_request_context, AuditEvent.all)).to be_truthy
end
+
+ it 'returns true for Packages::BuildInfo' do
+ expect(subject.available?(cursor_based_request_context, Packages::BuildInfo.all)).to be_truthy
+ end
end
context 'with other order-by columns' do
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index b076bb65fb5..f3dcdfe2a9d 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -9,8 +9,10 @@ RSpec.describe Gitlab::ProjectAuthorizations, feature_category: :system_access d
end
end
+ let(:service) { described_class.new(user) }
+
subject(:authorizations) do
- described_class.new(user).calculate
+ service.calculate
end
# Inline this shared example while cleaning up feature flag linear_project_authorization
@@ -421,9 +423,53 @@ RSpec.describe Gitlab::ProjectAuthorizations, feature_category: :system_access d
end
end
- context 'when feature_flag linear_project_authorization_is disabled' do
+ context 'it compares values for correctness' do
+ let_it_be(:user) { create(:user) }
+
+ context 'when values returned by the queries are the same' do
+ it 'logs a message indicating that the values are the same' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(event: 'linear_authorized_projects_check',
+ user_id: user.id,
+ matching_results: true)
+ service.calculate
+ end
+ end
+
+ context 'when values returned by queries are diffrent' do
+ before do
+ create(:project_authorization)
+ allow(service).to receive(:calculate_with_linear_query).and_return(ProjectAuthorization.all)
+ end
+
+ it 'logs a message indicating that the values are different' do
+ expect(Gitlab::AppJsonLogger).to receive(:warn).with(event: 'linear_authorized_projects_check',
+ user_id: user.id,
+ matching_results: false)
+ service.calculate
+ end
+ end
+ end
+
+ context 'when feature_flag linear_project_authorization is disabled' do
+ before do
+ stub_feature_flags(linear_project_authorization: false)
+ end
+
+ it_behaves_like 'project authorizations'
+ end
+
+ context 'when feature_flag compare_project_authorization_linear_cte is disabled' do
+ before do
+ stub_feature_flags(compare_project_authorization_linear_cte: false)
+ end
+
+ it_behaves_like 'project authorizations'
+ end
+
+ context 'when feature_flag linear_project_authorization and compare_project_authorization_linear_cte are disabled' do
before do
stub_feature_flags(linear_project_authorization: false)
+ stub_feature_flags(compare_project_authorization_linear_cte: false)
end
it_behaves_like 'project authorizations'
diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb
index 94b7e295167..9a2caeb7435 100644
--- a/spec/policies/user_policy_spec.rb
+++ b/spec/policies/user_policy_spec.rb
@@ -253,10 +253,12 @@ RSpec.describe UserPolicy do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:read_user_email_address) }
+ it { is_expected.to be_allowed(:admin_user_email_address) }
end
context 'when admin mode is disabled' do
it { is_expected.not_to be_allowed(:read_user_email_address) }
+ it { is_expected.not_to be_allowed(:admin_user_email_address) }
end
end
@@ -265,10 +267,12 @@ RSpec.describe UserPolicy do
subject { described_class.new(current_user, current_user) }
it { is_expected.to be_allowed(:read_user_email_address) }
+ it { is_expected.to be_allowed(:admin_user_email_address) }
end
context "requesting a different user's" do
it { is_expected.not_to be_allowed(:read_user_email_address) }
+ it { is_expected.not_to be_allowed(:admin_user_email_address) }
end
end
end
diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb
index f10150b819a..e776716bd2d 100644
--- a/spec/presenters/blob_presenter_spec.rb
+++ b/spec/presenters/blob_presenter_spec.rb
@@ -31,6 +31,32 @@ RSpec.describe BlobPresenter do
it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/update/#{blob.commit_id}/#{blob.path}") }
end
+ context 'when blob has ref_type' do
+ before do
+ blob.ref_type = 'heads'
+ end
+
+ describe '#web_url' do
+ it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}?ref_type=heads") }
+ end
+
+ describe '#web_path' do
+ it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}?ref_type=heads") }
+ end
+
+ describe '#edit_blob_path' do
+ it { expect(presenter.edit_blob_path).to eq("/#{project.full_path}/-/edit/#{blob.commit_id}/#{blob.path}?ref_type=heads") }
+ end
+
+ describe '#raw_path' do
+ it { expect(presenter.raw_path).to eq("/#{project.full_path}/-/raw/#{blob.commit_id}/#{blob.path}?ref_type=heads") }
+ end
+
+ describe '#replace_path' do
+ it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/update/#{blob.commit_id}/#{blob.path}?ref_type=heads") }
+ end
+ end
+
describe '#can_current_user_push_to_branch' do
let(:branch_exists) { true }
diff --git a/spec/presenters/tree_entry_presenter_spec.rb b/spec/presenters/tree_entry_presenter_spec.rb
index de84f36c5e6..0abf372b704 100644
--- a/spec/presenters/tree_entry_presenter_spec.rb
+++ b/spec/presenters/tree_entry_presenter_spec.rb
@@ -17,4 +17,20 @@ RSpec.describe TreeEntryPresenter do
describe '#web_path' do
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/tree/#{tree.commit_id}/#{tree.path}") }
end
+
+ context 'when blob has ref_type' do
+ before do
+ tree.ref_type = 'heads'
+ end
+
+ describe '.web_url' do
+ it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/tree/#{tree.commit_id}/#{tree.path}?ref_type=heads") }
+ end
+
+ describe '#web_path' do
+ it {
+ expect(presenter.web_path).to eq("/#{project.full_path}/-/tree/#{tree.commit_id}/#{tree.path}?ref_type=heads")
+ }
+ end
+ end
end
diff --git a/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb b/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb
new file mode 100644
index 00000000000..1db6f83ce4f
--- /dev/null
+++ b/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Setting namespace commit email', feature_category: :user_profile do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:group) { create(:group, :public) }
+ let(:email) { create(:email, :confirmed, user: current_user) }
+ let(:input) { {} }
+ let(:namespace_id) { group.to_global_id }
+ let(:email_id) { email.to_global_id }
+
+ let(:resource_or_permission_error) do
+ "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ end
+
+ let(:mutation) do
+ variables = {
+ namespace_id: namespace_id,
+ email_id: email_id
+ }
+ graphql_mutation(:user_set_namespace_commit_email, variables.merge(input),
+ <<-QL.strip_heredoc
+ namespaceCommitEmail {
+ email {
+ id
+ }
+ }
+ errors
+ QL
+ )
+ end
+
+ def mutation_response
+ graphql_mutation_response(:user_set_namespace_commit_email)
+ end
+
+ shared_examples 'success' do
+ it 'creates a namespace commit email' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response.dig('namespaceCommitEmail', 'email', 'id')).to eq(email.to_global_id.to_s)
+ expect(graphql_errors).to be_nil
+ end
+ end
+
+ before do
+ group.add_reporter(current_user)
+ end
+
+ context 'when current_user is nil' do
+ it 'returns the top level error' do
+ post_graphql_mutation(mutation, current_user: nil)
+
+ expect(graphql_errors.first).to match a_hash_including(
+ 'message' => resource_or_permission_error)
+ end
+ end
+
+ context 'when the user cannot access the namespace' do
+ let(:namespace_id) { create(:group).to_global_id }
+
+ it 'returns the top level error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(graphql_errors).not_to be_empty
+ expect(graphql_errors.first).to match a_hash_including(
+ 'message' => resource_or_permission_error)
+ end
+ end
+
+ context 'when the service returns an error' do
+ let(:email_id) { create(:email).to_global_id }
+
+ it 'returns the error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['errors']).to contain_exactly("Email must be provided.")
+ expect(mutation_response['namespaceCommitEmail']).to be_nil
+ end
+ end
+
+ context 'when namespace is a group' do
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a user' do
+ let(:namespace_id) { current_user.namespace.to_global_id }
+
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a project' do
+ let_it_be(:project) { create(:project) }
+
+ let(:namespace_id) { project.project_namespace.to_global_id }
+
+ before do
+ project.add_reporter(current_user)
+ end
+
+ it_behaves_like 'success'
+ end
+end
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index c003ae9cd48..b84b7e9c52d 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -3,9 +3,11 @@
require 'spec_helper'
RSpec.describe API::ProjectPackages, feature_category: :package_registry do
- let_it_be(:project) { create(:project, :public) }
+ using RSpec::Parameterized::TableSyntax
- let(:user) { create(:user) }
+ let_it_be_with_reload(:project) { create(:project, :public) }
+
+ let_it_be(:user) { create(:user) }
let!(:package1) { create(:npm_package, :last_downloaded_at, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
let(:package_url) { "/projects/#{project.id}/packages/#{package1.id}" }
let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') }
@@ -101,7 +103,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
end
context 'project is private' do
- let(:project) { create(:project, :private) }
+ let_it_be(:project) { create(:project, :private) }
context 'for unauthenticated user' do
it_behaves_like 'rejects packages access', :project, :no_type, :not_found
@@ -235,7 +237,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
expect do
get api(package_url, user)
- end.not_to exceed_query_limit(control)
+ end.not_to exceed_query_limit(control).with_threshold(4)
end
end
@@ -286,7 +288,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
end
context 'project is private' do
- let(:project) { create(:project, :private) }
+ let_it_be(:project) { create(:project, :private) }
it 'returns 404 for non authenticated user' do
get api(package_url)
@@ -362,6 +364,235 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
end
end
+ describe 'GET /projects/:id/packages/:package_id/pipelines' do
+ let(:package_pipelines_url) { "/projects/#{project.id}/packages/#{package1.id}/pipelines" }
+
+ let(:tokens) do
+ {
+ personal_access_token: personal_access_token.token,
+ job_token: job.token
+ }
+ end
+
+ let_it_be(:personal_access_token) { create(:personal_access_token) }
+ let_it_be(:user) { personal_access_token.user }
+ let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
+ let(:headers) { {} }
+
+ subject { get api(package_pipelines_url) }
+
+ shared_examples 'returns package pipelines' do |expected_status|
+ it 'returns the first page of package pipelines' do
+ subject
+
+ expect(response).to have_gitlab_http_status(expected_status)
+ expect(response).to match_response_schema('public_api/v4/packages/pipelines')
+ expect(json_response.length).to eq(3)
+ expect(json_response.pluck('id')).to eq(pipelines.reverse.map(&:id))
+ end
+ end
+
+ context 'without the need for a license' do
+ context 'when the package does not exist' do
+ let(:package_pipelines_url) { "/projects/#{project.id}/packages/0/pipelines" }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ context 'when there are no pipelines for the package' do
+ let(:package_pipelines_url) { "/projects/#{project.id}/packages/#{package2.id}/pipelines" }
+
+ it 'returns an empty response' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(response).to match_response_schema('public_api/v4/packages/pipelines')
+ expect(json_response.length).to eq(0)
+ end
+ end
+
+ context 'with valid package and pipelines' do
+ let!(:pipelines) do
+ create_list(:ci_pipeline, 3, user: user, project: project).each do |pipeline|
+ create(:package_build_info, package: package1, pipeline: pipeline)
+ end
+ end
+
+ where(:visibility, :user_role, :member, :token_type, :valid_token, :shared_examples_name, :expected_status) do
+ :public | :developer | true | :personal_access_token | true | 'returns package pipelines' | :success
+ :public | :guest | true | :personal_access_token | true | 'returns package pipelines' | :success
+ :public | :developer | true | :personal_access_token | false | 'returning response status' | :unauthorized
+ :public | :guest | true | :personal_access_token | false | 'returning response status' | :unauthorized
+ :public | :developer | false | :personal_access_token | true | 'returns package pipelines' | :success
+ :public | :guest | false | :personal_access_token | true | 'returns package pipelines' | :success
+ :public | :developer | false | :personal_access_token | false | 'returning response status' | :unauthorized
+ :public | :guest | false | :personal_access_token | false | 'returning response status' | :unauthorized
+ :public | :anonymous | false | nil | true | 'returns package pipelines' | :success
+ :private | :developer | true | :personal_access_token | true | 'returns package pipelines' | :success
+ :private | :guest | true | :personal_access_token | true | 'returning response status' | :forbidden
+ :private | :developer | true | :personal_access_token | false | 'returning response status' | :unauthorized
+ :private | :guest | true | :personal_access_token | false | 'returning response status' | :unauthorized
+ :private | :developer | false | :personal_access_token | true | 'returning response status' | :not_found
+ :private | :guest | false | :personal_access_token | true | 'returning response status' | :not_found
+ :private | :developer | false | :personal_access_token | false | 'returning response status' | :unauthorized
+ :private | :guest | false | :personal_access_token | false | 'returning response status' | :unauthorized
+ :private | :anonymous | false | nil | true | 'returning response status' | :not_found
+ :public | :developer | true | :job_token | true | 'returns package pipelines' | :success
+ :public | :guest | true | :job_token | true | 'returns package pipelines' | :success
+ :public | :developer | true | :job_token | false | 'returning response status' | :unauthorized
+ :public | :guest | true | :job_token | false | 'returning response status' | :unauthorized
+ :public | :developer | false | :job_token | true | 'returns package pipelines' | :success
+ :public | :guest | false | :job_token | true | 'returns package pipelines' | :success
+ :public | :developer | false | :job_token | false | 'returning response status' | :unauthorized
+ :public | :guest | false | :job_token | false | 'returning response status' | :unauthorized
+ :private | :developer | true | :job_token | true | 'returns package pipelines' | :success
+ # TODO uncomment the spec below when https://gitlab.com/gitlab-org/gitlab/-/issues/370998 is resolved
+ # :private | :guest | true | :job_token | true | 'returning response status' | :forbidden
+ :private | :developer | true | :job_token | false | 'returning response status' | :unauthorized
+ :private | :guest | true | :job_token | false | 'returning response status' | :unauthorized
+ :private | :developer | false | :job_token | true | 'returning response status' | :not_found
+ :private | :guest | false | :job_token | true | 'returning response status' | :not_found
+ :private | :developer | false | :job_token | false | 'returning response status' | :unauthorized
+ :private | :guest | false | :job_token | false | 'returning response status' | :unauthorized
+ end
+
+ with_them do
+ subject { get api(package_pipelines_url), headers: headers }
+
+ let(:invalid_token) { 'invalid-token123' }
+ let(:token) { valid_token ? tokens[token_type] : invalid_token }
+ let(:headers) do
+ case token_type
+ when :personal_access_token
+ { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => token }
+ when :job_token
+ { Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER => token }
+ when nil
+ {}
+ end
+ end
+
+ before do
+ project.update!(visibility: visibility.to_s)
+ project.send("add_#{user_role}", user) if member && user_role != :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:expected_status]
+ end
+ end
+
+ context 'pagination' do
+ shared_context 'setup pipeline records' do
+ let!(:pipelines) do
+ create_list(:package_build_info, 21, :with_pipeline, package: package1)
+ end
+ end
+
+ shared_examples 'returns the default number of pipelines' do
+ it do
+ subject
+
+ expect(json_response.size).to eq(20)
+ end
+ end
+
+ shared_examples 'returns an error about the invalid per_page value' do
+ it do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to match(/per_page does not have a valid value/)
+ end
+ end
+
+ context 'without pagination params' do
+ include_context 'setup pipeline records'
+
+ it_behaves_like 'returns the default number of pipelines'
+ end
+
+ context 'with valid per_page value' do
+ let(:per_page) { 11 }
+
+ subject { get api(package_pipelines_url, user), params: { per_page: per_page } }
+
+ include_context 'setup pipeline records'
+
+ it 'returns the correct number of pipelines' do
+ subject
+
+ expect(json_response.size).to eq(per_page)
+ end
+ end
+
+ context 'with invalid pagination params' do
+ subject { get api(package_pipelines_url, user), params: { per_page: per_page } }
+
+ context 'with non-positive per_page' do
+ let(:per_page) { -2 }
+
+ it_behaves_like 'returns an error about the invalid per_page value'
+ end
+
+ context 'with a too high value for per_page' do
+ let(:per_page) { 21 }
+
+ it_behaves_like 'returns an error about the invalid per_page value'
+ end
+ end
+
+ context 'with valid pagination params' do
+ let_it_be(:package1) { create(:npm_package, :last_downloaded_at, project: project) }
+ let_it_be(:build_info1) { create(:package_build_info, :with_pipeline, package: package1) }
+ let_it_be(:build_info2) { create(:package_build_info, :with_pipeline, package: package1) }
+ let_it_be(:build_info3) { create(:package_build_info, :with_pipeline, package: package1) }
+
+ let(:pipeline1) { build_info1.pipeline }
+ let(:pipeline2) { build_info2.pipeline }
+ let(:pipeline3) { build_info3.pipeline }
+
+ let(:per_page) { 2 }
+
+ context 'with no cursor supplied' do
+ subject { get api(package_pipelines_url, user), params: { per_page: per_page } }
+
+ it 'returns first 2 pipelines' do
+ subject
+
+ expect(json_response.pluck('id')).to contain_exactly(pipeline3.id, pipeline2.id)
+ end
+ end
+
+ context 'with a cursor parameter' do
+ let(:cursor) { Base64.urlsafe_encode64(Gitlab::Json.dump(cursor_attributes)) }
+
+ subject { get api(package_pipelines_url, user), params: { per_page: per_page, cursor: cursor } }
+
+ before do
+ subject
+ end
+
+ context 'with a cursor for the next page' do
+ let(:cursor_attributes) { { "id" => build_info2.id, "_kd" => "n" } }
+
+ it 'returns the next page of records' do
+ expect(json_response.pluck('id')).to contain_exactly(pipeline1.id)
+ end
+ end
+
+ context 'with a cursor for the previous page' do
+ let(:cursor_attributes) { { "id" => build_info1.id, "_kd" => "p" } }
+
+ it 'returns the previous page of records' do
+ expect(json_response.pluck('id')).to contain_exactly(pipeline3.id, pipeline2.id)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
describe 'DELETE /projects/:id/packages/:package_id' do
context 'without the need for a license' do
context 'project is public' do
@@ -379,7 +610,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
end
context 'project is private' do
- let(:project) { create(:project, :private) }
+ let_it_be(:project) { create(:project, :private) }
before do
expect(::Packages::Maven::Metadata::SyncWorker).not_to receive(:perform_async)
diff --git a/spec/services/users/set_namespace_commit_email_service_spec.rb b/spec/services/users/set_namespace_commit_email_service_spec.rb
new file mode 100644
index 00000000000..4f64d454ecb
--- /dev/null
+++ b/spec/services/users/set_namespace_commit_email_service_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::SetNamespaceCommitEmailService, feature_category: :user_profile do
+ include AfterNextHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:email) { create(:email, user: user) }
+ let_it_be(:existing_achievement) { create(:achievement, namespace: group) }
+
+ let(:namespace) { group }
+ let(:current_user) { user }
+ let(:target_user) { user }
+ let(:email_id) { email.id }
+ let(:params) { { user: target_user } }
+ let(:service) { described_class.new(current_user, namespace, email_id, params) }
+
+ before_all do
+ group.add_reporter(user)
+ end
+
+ shared_examples 'success' do
+ it 'creates namespace commit email' do
+ result = service.execute
+
+ expect(result.payload[:namespace_commit_email]).to be_a(Users::NamespaceCommitEmail)
+ expect(result.payload[:namespace_commit_email]).to be_persisted
+ end
+ end
+
+ describe '#execute' do
+ context 'when current_user is not provided' do
+ let(:current_user) { nil }
+
+ it 'returns error message' do
+ expect(service.execute.message)
+ .to eq("User doesn't exist or you don't have permission to change namespace commit emails.")
+ end
+ end
+
+ context 'when current_user does not have permission to change namespace commit emails' do
+ let(:target_user) { create(:user) }
+
+ it 'returns error message' do
+ expect(service.execute.message)
+ .to eq("User doesn't exist or you don't have permission to change namespace commit emails.")
+ end
+ end
+
+ context 'when target_user does not have permission to access the namespace' do
+ let(:namespace) { create(:group) }
+
+ it 'returns error message' do
+ expect(service.execute.message).to eq("Namespace doesn't exist or you don't have permission.")
+ end
+ end
+
+ context 'when namespace is not provided' do
+ let(:namespace) { nil }
+
+ it 'returns error message' do
+ expect(service.execute.message).to eq('Namespace must be provided.')
+ end
+ end
+
+ context 'when target user is not current user' do
+ context 'when current user is an admin' do
+ let(:current_user) { create(:user, :admin) }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'creates namespace commit email' do
+ result = service.execute
+
+ expect(result.payload[:namespace_commit_email]).to be_a(Users::NamespaceCommitEmail)
+ expect(result.payload[:namespace_commit_email]).to be_persisted
+ end
+ end
+
+ context 'when admin mode is not enabled' do
+ it 'returns error message' do
+ expect(service.execute.message)
+ .to eq("User doesn't exist or you don't have permission to change namespace commit emails.")
+ end
+ end
+ end
+
+ context 'when current user is not an admin' do
+ let(:current_user) { create(:user) }
+
+ it 'returns error message' do
+ expect(service.execute.message)
+ .to eq("User doesn't exist or you don't have permission to change namespace commit emails.")
+ end
+ end
+ end
+
+ context 'when namespace commit email does not exist' do
+ context 'when email_id is not provided' do
+ let(:email_id) { nil }
+
+ it 'returns error message' do
+ expect(service.execute.message).to eq('Email must be provided.')
+ end
+ end
+
+ context 'when model save fails' do
+ before do
+ allow_next(::Users::NamespaceCommitEmail).to receive(:save).and_return(false)
+ end
+
+ it 'returns error message' do
+ expect(service.execute.message).to eq('Failed to save namespace commit email.')
+ end
+ end
+
+ context 'when namepsace is a group' do
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a user' do
+ let(:namespace) { current_user.namespace }
+
+ it_behaves_like 'success'
+ end
+
+ context 'when namespace is a project' do
+ let_it_be(:project) { create(:project) }
+
+ let(:namespace) { project.project_namespace }
+
+ before do
+ project.add_reporter(current_user)
+ end
+
+ it_behaves_like 'success'
+ end
+ end
+
+ context 'when namespace commit email already exists' do
+ let!(:existing_namespace_commit_email) do
+ create(:namespace_commit_email,
+ user: target_user,
+ namespace: namespace,
+ email: create(:email, user: target_user))
+ end
+
+ context 'when email_id is not provided' do
+ let(:email_id) { nil }
+
+ it 'destroys the namespace commit email' do
+ result = service.execute
+
+ expect(result.message).to be_nil
+ expect(result.payload[:namespace_commit_email]).to be_nil
+ end
+ end
+
+ context 'and email_id is provided' do
+ let(:email_id) { create(:email, user: current_user).id }
+
+ it 'updates namespace commit email' do
+ result = service.execute
+
+ existing_namespace_commit_email.reload
+
+ expect(result.payload[:namespace_commit_email]).to eq(existing_namespace_commit_email)
+ expect(existing_namespace_commit_email.email_id).to eq(email_id)
+ end
+ end
+
+ context 'when model save fails' do
+ before do
+ allow_any_instance_of(::Users::NamespaceCommitEmail).to receive(:save).and_return(false) # rubocop:disable RSpec/AnyInstanceOf
+ end
+
+ it 'returns generic error message' do
+ expect(service.execute.message).to eq('Failed to save namespace commit email.')
+ end
+
+ context 'with model errors' do
+ before do
+ allow_any_instance_of(::Users::NamespaceCommitEmail).to receive_message_chain(:errors, :empty?).and_return(false) # rubocop:disable RSpec/AnyInstanceOf
+ allow_any_instance_of(::Users::NamespaceCommitEmail).to receive_message_chain(:errors, :full_messages, :to_sentence).and_return('Model error') # rubocop:disable RSpec/AnyInstanceOf
+ end
+
+ it 'returns the model error message' do
+ expect(service.execute.message).to eq('Model error')
+ end
+ end
+ end
+ end
+ end
+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 ab57f4e2c55..128bd28410c 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -80,10 +80,13 @@ RSpec.shared_examples 'work items comments' do |type|
it 'shows work item note actions' do
set_comment
- click_button "Comment"
-
+ send_keys([modifier_key, :enter])
wait_for_requests
+ page.within(".main-notes-list") do
+ expect(page).to have_content comment
+ end
+
page.within('.timeline-entry.note.note-wrapper.note-comment:last-child') do
expect(page).to have_selector('[data-testid="work-item-note-actions"]')