diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-21 15:09:17 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-21 15:09:17 +0300 |
commit | 0c4570435d417b69efd433057f95f01810618837 (patch) | |
tree | 4e402832206b83da2d73671977c1e5f7cae9074a /spec | |
parent | 49abdb108a4d3c3f2ef9b27c7c4dcde43da1016a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
44 files changed, 785 insertions, 255 deletions
diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 3476c7b8465..4772c3f3487 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -298,4 +298,15 @@ RSpec.describe Oauth::AuthorizationsController do it 'includes Two-factor enforcement concern' do expect(described_class.included_modules.include?(EnforcesTwoFactorAuthentication)).to eq(true) end + + describe 'Gon variables' do + it 'adds Gon variables' do + expect(controller).to receive(:add_gon_variables) + get :new, params: params + end + + it 'includes GonHelper module' do + expect(controller).to be_a(Gitlab::GonHelper) + end + end end diff --git a/spec/features/groups/milestones/gfm_autocomplete_spec.rb b/spec/features/groups/milestones/gfm_autocomplete_spec.rb index 8df097dde88..9245323d1f7 100644 --- a/spec/features/groups/milestones/gfm_autocomplete_spec.rb +++ b/spec/features/groups/milestones/gfm_autocomplete_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do + include Features::AutocompleteHelpers + let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } let_it_be(:group) { create(:group, name: 'Ancestor') } let_it_be(:project) { create(:project, :repository, group: group) } @@ -69,10 +71,6 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do private - def find_autocomplete_menu - find('.atwho-view ul', visible: true) - end - def expect_autocomplete_entry(entry) page.within('.atwho-container') do expect(page).to have_content(entry) diff --git a/spec/features/groups/participants_autocomplete_spec.rb b/spec/features/groups/participants_autocomplete_spec.rb new file mode 100644 index 00000000000..a94f95c3ced --- /dev/null +++ b/spec/features/groups/participants_autocomplete_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Group member autocomplete', :js, feature_category: :groups_and_projects do + include Features::AutocompleteHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } + + before_all do + group.add_developer user + end + + before do + sign_in(user) + end + + context 'when editing description of a group milestone' do + let_it_be(:noteable) { create(:milestone, group: group) } + + it 'suggests group members' do + visit edit_group_milestone_path(group, noteable) + + fill_in 'Description', with: '@' + + expect(find_autocomplete_menu).to have_text(user.username) + end + + context 'for a member of a private group invited to the group' do + let_it_be(:private_group) { create(:group, :private) } + let_it_be(:private_group_member) { create(:user, username: 'private-a') } + + before_all do + private_group.add_developer private_group_member + + create(:group_group_link, shared_group: group, shared_with_group: private_group) + end + + it 'suggests member of private group as well' do + visit edit_group_milestone_path(group, noteable) + + fill_in 'Description', with: '@' + + expect(find_autocomplete_menu).to have_text(private_group_member.username) + expect(find_autocomplete_menu).to have_text(user.username) + end + end + end +end diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 665c7307231..a1f2275df6e 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do include CookieHelper + include Features::AutocompleteHelpers let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') } @@ -453,12 +454,4 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do wait_for_requests end - - def find_autocomplete_menu - find('.atwho-view ul', visible: true) - end - - def find_highlighted_autocomplete_item - find('.atwho-view li.cur', visible: true) - end end diff --git a/spec/features/issues/user_comments_on_issue_spec.rb b/spec/features/issues/user_comments_on_issue_spec.rb index 3ace560fb40..d954a1d15ff 100644 --- a/spec/features/issues/user_comments_on_issue_spec.rb +++ b/spec/features/issues/user_comments_on_issue_spec.rb @@ -3,6 +3,7 @@ require "spec_helper" RSpec.describe "User comments on issue", :js, feature_category: :team_planning do + include Features::AutocompleteHelpers include Features::NotesHelpers let_it_be(:project) { create(:project, :public) } @@ -92,10 +93,4 @@ RSpec.describe "User comments on issue", :js, feature_category: :team_planning d end end end - - private - - def find_highlighted_autocomplete_item - find('.atwho-view li.cur', visible: true) - end end diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index 5da9f4a1f19..4bdef20304a 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -10,9 +10,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js, feat let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, note: "| Markdown | Table |\n|-------|---------|\n| first | second |") } let(:path) { "files/ruby/popen.rb" } let(:position) do - build(:text_diff_position, - file: path, old_line: nil, new_line: 9, - diff_refs: merge_request.diff_refs) + build(:text_diff_position, file: path, old_line: nil, new_line: 9, diff_refs: merge_request.diff_refs) end before do diff --git a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb index 5c41ac79552..cb57f1fd549 100644 --- a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb +++ b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Merge request > User resolves outdated diff discussions', - :js, feature_category: :code_review_workflow do + :js, feature_category: :code_review_workflow do let(:project) { create(:project, :repository, :public) } let(:merge_request) do @@ -30,17 +30,21 @@ RSpec.describe 'Merge request > User resolves outdated diff discussions', end let!(:outdated_discussion) do - create(:diff_note_on_merge_request, - project: project, - noteable: merge_request, - position: outdated_position).to_discussion + create( + :diff_note_on_merge_request, + project: project, + noteable: merge_request, + position: outdated_position + ).to_discussion end let!(:current_discussion) do - create(:diff_note_on_merge_request, - noteable: merge_request, - project: project, - position: current_position).to_discussion + create( + :diff_note_on_merge_request, + noteable: merge_request, + project: project, + position: current_position + ).to_discussion end before do diff --git a/spec/features/merge_request/user_resolves_wip_mr_spec.rb b/spec/features/merge_request/user_resolves_wip_mr_spec.rb index 01cc6bd5167..15a7755429b 100644 --- a/spec/features/merge_request/user_resolves_wip_mr_spec.rb +++ b/spec/features/merge_request/user_resolves_wip_mr_spec.rb @@ -6,17 +6,23 @@ RSpec.describe 'Merge request > User resolves Draft', :js, feature_category: :co let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } let(:merge_request) do - create(:merge_request_with_diffs, source_project: project, - author: user, - title: 'Draft: Bug NS-04', - merge_params: { force_remove_source_branch: '1' }) + create( + :merge_request_with_diffs, + source_project: project, + author: user, + title: 'Draft: Bug NS-04', + merge_params: { force_remove_source_branch: '1' } + ) end let(:pipeline) do - create(:ci_pipeline, project: project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch, - head_pipeline_of: merge_request) + create( + :ci_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch, + head_pipeline_of: merge_request + ) end before do diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb index 6dcebad300c..44660b247a1 100644 --- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb +++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb @@ -115,8 +115,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js, feature_categ context 'with stop action' do let(:manual) do - create(:ci_build, :manual, pipeline: pipeline, - name: 'close_app', environment: environment.name) + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app', environment: environment.name) end before do @@ -146,8 +145,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js, feature_categ context 'with stop action with the review_apps_redeploy_mr_widget feature flag turned on' do let(:manual) do - create(:ci_build, :manual, pipeline: pipeline, - name: 'close_app', environment: environment.name) + create(:ci_build, :manual, pipeline: pipeline, name: 'close_app', environment: environment.name) end before do diff --git a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb index 338e4329190..4cce40972e9 100644 --- a/spec/features/merge_request/user_sees_discussions_navigation_spec.rb +++ b/spec/features/merge_request/user_sees_discussions_navigation_spec.rb @@ -13,27 +13,30 @@ RSpec.describe 'Merge request > User sees discussions navigation', :js, feature_ describe 'Code discussions' do let!(:position) do - build(:text_diff_position, :added, - file: "files/images/wm.svg", - new_line: 1, - diff_refs: merge_request.diff_refs + build( + :text_diff_position, :added, + file: "files/images/wm.svg", + new_line: 1, + diff_refs: merge_request.diff_refs ) end let!(:first_discussion) do - create(:diff_note_on_merge_request, - noteable: merge_request, - project: project, - position: position - ).to_discussion + create( + :diff_note_on_merge_request, + noteable: merge_request, + project: project, + position: position + ).to_discussion end let!(:second_discussion) do - create(:diff_note_on_merge_request, - noteable: merge_request, - project: project, - position: position - ).to_discussion + create( + :diff_note_on_merge_request, + noteable: merge_request, + project: project, + position: position + ).to_discussion end let(:first_discussion_selector) { ".discussion[data-discussion-id='#{first_discussion.id}']" } @@ -74,11 +77,12 @@ RSpec.describe 'Merge request > User sees discussions navigation', :js, feature_ context 'with resolved threads' do let!(:resolved_discussion) do - create(:diff_note_on_merge_request, - noteable: merge_request, - project: project, - position: position - ).to_discussion + create( + :diff_note_on_merge_request, + noteable: merge_request, + project: project, + position: position + ).to_discussion end let(:resolved_discussion_selector) { ".discussion[data-discussion-id='#{resolved_discussion.id}']" } @@ -92,7 +96,7 @@ RSpec.describe 'Merge request > User sees discussions navigation', :js, feature_ end it 'excludes resolved threads during navigation', - quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383687' do + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/383687' do goto_next_thread goto_next_thread goto_next_thread diff --git a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb index 476be5ab599..9955c13b769 100644 --- a/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb +++ b/spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe 'Merge request > User sees merge button depending on unresolved threads', :js, -feature_category: :code_review_workflow do + feature_category: :code_review_workflow do let(:project) { create(:project, :repository) } let(:user) { project.creator } let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) } diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb index ca12e0e2b65..fba25b41b83 100644 --- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb @@ -36,11 +36,13 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when a user created a merge request in the parent project' do let!(:merge_request) do - create(:merge_request, - source_project: project, - target_project: project, - source_branch: 'feature', - target_branch: 'master') + create( + :merge_request, + source_project: project, + target_project: project, + source_branch: 'feature', + target_branch: 'master' + ) end let!(:push_pipeline) do @@ -263,11 +265,13 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request', context 'when a user created a merge request from a forked project to the parent project', :sidekiq_might_not_need_inline do let(:merge_request) do - create(:merge_request, - source_project: forked_project, - target_project: project, - source_branch: 'feature', - target_branch: 'master') + create( + :merge_request, + source_project: forked_project, + target_project: project, + source_branch: 'feature', + target_branch: 'master' + ) end let!(:push_pipeline) do diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index cb56e79fcc0..33694c056a0 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -118,12 +118,15 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: context 'view merge request with failed GitLab CI pipelines' do before do commit_status = create(:commit_status, project: project, status: 'failed') - pipeline = create(:ci_pipeline, project: project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch, - status: 'failed', - statuses: [commit_status], - head_pipeline_of: merge_request) + pipeline = create( + :ci_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch, + status: 'failed', + statuses: [commit_status], + head_pipeline_of: merge_request + ) create(:ci_build, :pending, pipeline: pipeline) visit project_merge_request_path(project, merge_request) @@ -278,12 +281,15 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: context 'view merge request with MWBS button' do before do commit_status = create(:commit_status, project: project, status: 'pending') - pipeline = create(:ci_pipeline, project: project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch, - status: 'pending', - statuses: [commit_status], - head_pipeline_of: merge_request) + pipeline = create( + :ci_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch, + status: 'pending', + statuses: [commit_status], + head_pipeline_of: merge_request + ) create(:ci_build, :pending, pipeline: pipeline) visit project_merge_request_path(project, merge_request) @@ -298,9 +304,12 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: context 'view merge request where there is no pipeline yet' do before do - pipeline = create(:ci_pipeline, project: project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch) + pipeline = create( + :ci_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch + ) create(:ci_build, pipeline: pipeline) visit project_merge_request_path(project, merge_request) @@ -510,11 +519,13 @@ RSpec.describe 'Merge request > User sees merge widget', :js, feature_category: context 'when merge request has test reports' do let!(:head_pipeline) do - create(:ci_pipeline, - :success, - project: project, - ref: merge_request.source_branch, - sha: merge_request.diff_head_sha) + create( + :ci_pipeline, + :success, + project: project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha + ) end let!(:build) { create(:ci_build, :success, pipeline: head_pipeline, project: project) } diff --git a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb index fac0a84f155..7b8ac50f1ae 100644 --- a/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb @@ -3,16 +3,19 @@ require 'spec_helper' RSpec.describe 'Merge request > User sees MR from deleted forked project', - :js, feature_category: :code_review_workflow do + :js, feature_category: :code_review_workflow do include ProjectForksHelper let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } let(:forked_project) { fork_project(project, nil, repository: true) } let!(:merge_request) do - create(:merge_request_with_diffs, source_project: forked_project, - target_project: project, - description: 'Test merge request') + create( + :merge_request_with_diffs, + source_project: forked_project, + target_project: project, + description: 'Test merge request' + ) end before do diff --git a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb index 9b46cf37648..29a76768774 100644 --- a/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb +++ b/spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb @@ -6,7 +6,7 @@ require 'spec_helper' # message to be shown by JavaScript when the source branch was deleted. # Please do not remove ":js". RSpec.describe 'Merge request > User sees MR with deleted source branch', - :js, feature_category: :code_review_workflow do + :js, feature_category: :code_review_workflow do let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let(:user) { project.creator } diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb index ac195dd9873..92bedc47718 100644 --- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb @@ -9,15 +9,21 @@ RSpec.describe 'Merge request > User sees notes from forked project', :js, featu let(:user) { project.creator } let(:forked_project) { fork_project(project, nil, repository: true) } let!(:merge_request) do - create(:merge_request_with_diffs, source_project: forked_project, - target_project: project, - description: 'Test merge request') + create( + :merge_request_with_diffs, + source_project: forked_project, + target_project: project, + description: 'Test merge request' + ) end before do - create(:note_on_commit, note: 'A commit comment', - project: forked_project, - commit_id: merge_request.commit_shas.first) + create( + :note_on_commit, + note: 'A commit comment', + project: forked_project, + commit_id: merge_request.commit_shas.first + ) sign_in(user) end diff --git a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb index 0816b14f9a5..5801e8a1a11 100644 --- a/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb @@ -3,23 +3,28 @@ require 'spec_helper' RSpec.describe 'Merge request > User sees pipelines from forked project', :js, -feature_category: :continuous_integration do + feature_category: :continuous_integration do include ProjectForksHelper let(:target_project) { create(:project, :public, :repository) } let(:user) { target_project.creator } let(:forked_project) { fork_project(target_project, nil, repository: true) } let!(:merge_request) do - create(:merge_request_with_diffs, source_project: forked_project, - target_project: target_project, - description: 'Test merge request') + create( + :merge_request_with_diffs, + source_project: forked_project, + target_project: target_project, + description: 'Test merge request' + ) end let(:pipeline) do - create(:ci_pipeline, - project: forked_project, - sha: merge_request.diff_head_sha, - ref: merge_request.source_branch) + create( + :ci_pipeline, + project: forked_project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch + ) end before do diff --git a/spec/features/merge_request/user_sees_pipelines_spec.rb b/spec/features/merge_request/user_sees_pipelines_spec.rb index faa46ff4df1..88c718fac13 100644 --- a/spec/features/merge_request/user_sees_pipelines_spec.rb +++ b/spec/features/merge_request/user_sees_pipelines_spec.rb @@ -15,11 +15,13 @@ RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :co context 'with pipelines' do let!(:pipeline) do - create(:ci_pipeline, - :success, - project: merge_request.source_project, - ref: merge_request.source_branch, - sha: merge_request.diff_head_sha) + create( + :ci_pipeline, + :success, + project: merge_request.source_project, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha + ) end let!(:manual_job) { create(:ci_build, :manual, name: 'job1', stage: 'deploy', pipeline: pipeline) } @@ -116,9 +118,14 @@ RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :co let_it_be(:reporter_in_parent_and_developer_in_fork) { create(:user) } let(:merge_request) do - create(:merge_request, :with_detached_merge_request_pipeline, - source_project: forked_project, source_branch: 'feature', - target_project: parent_project, target_branch: 'master') + create( + :merge_request, + :with_detached_merge_request_pipeline, + source_project: forked_project, + source_branch: 'feature', + target_project: parent_project, + target_branch: 'master' + ) end let(:config) do diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index 91f8fd13681..715cc2f73be 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -57,9 +57,9 @@ RSpec.describe 'Merge request > User sees versions', :js, feature_category: :cod end it_behaves_like 'allows commenting', - file_name: '.gitmodules', - line_text: '[submodule "six"]', - comment: 'Typo, please fix.' + file_name: '.gitmodules', + line_text: '[submodule "six"]', + comment: 'Typo, please fix.' end describe 'switch between versions' do @@ -105,9 +105,9 @@ RSpec.describe 'Merge request > User sees versions', :js, feature_category: :cod end it_behaves_like 'allows commenting', - file_name: '.gitmodules', - line_text: 'path = six', - comment: 'Typo, please fix.' + file_name: '.gitmodules', + line_text: 'path = six', + comment: 'Typo, please fix.' end describe 'compare with older version' do @@ -172,9 +172,9 @@ RSpec.describe 'Merge request > User sees versions', :js, feature_category: :cod end it_behaves_like 'allows commenting', - file_name: '.gitmodules', - line_text: '[submodule "gitlab-shell"]', - comment: 'Typo, please fix.' + file_name: '.gitmodules', + line_text: '[submodule "gitlab-shell"]', + comment: 'Typo, please fix.' end describe 'compare with same version' do @@ -239,8 +239,8 @@ RSpec.describe 'Merge request > User sees versions', :js, feature_category: :cod end it_behaves_like 'allows commenting', - file_name: 'files/ruby/popen.rb', - line_text: 'RuntimeError', - comment: 'Typo, please fix.' + file_name: 'files/ruby/popen.rb', + line_text: 'RuntimeError', + comment: 'Typo, please fix.' end end diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index 084bf609a0d..d8501116134 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Member autocomplete', :js, feature_category: :groups_and_projects do + include Features::AutocompleteHelpers + let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:user) { create(:user) } let_it_be(:author) { create(:user) } @@ -85,10 +87,4 @@ RSpec.describe 'Member autocomplete', :js, feature_category: :groups_and_project include_examples "open suggestions when typing @", 'commit' end - - private - - def find_autocomplete_menu - find('.atwho-view ul', visible: true) - end end diff --git a/spec/features/projects/milestones/gfm_autocomplete_spec.rb b/spec/features/projects/milestones/gfm_autocomplete_spec.rb index d4ce10b5cb5..0705cdd0d9e 100644 --- a/spec/features/projects/milestones/gfm_autocomplete_spec.rb +++ b/spec/features/projects/milestones/gfm_autocomplete_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do + include Features::AutocompleteHelpers + let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') } let_it_be(:group) { create(:group, name: 'Ancestor') } let_it_be(:project) { create(:project, :repository, group: group) } @@ -68,10 +70,6 @@ RSpec.describe 'GFM autocomplete', :js, feature_category: :team_planning do private - def find_autocomplete_menu - find('.atwho-view ul', visible: true) - end - def expect_autocomplete_entry(entry) page.within('.atwho-container') do expect(page).to have_content(entry) diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js index c9238c4b636..6ef34504da7 100644 --- a/spec/frontend/error_tracking/components/error_details_spec.js +++ b/spec/frontend/error_tracking/components/error_details_spec.js @@ -6,6 +6,9 @@ import { GlFormInput, GlAlert, GlSprintf, + GlDisclosureDropdown, + GlDisclosureDropdownItem, + GlDisclosureDropdownGroup, } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; @@ -46,7 +49,13 @@ describe('ErrorDetails', () => { function mountComponent({ integratedErrorTrackingEnabled = false } = {}) { wrapper = shallowMount(ErrorDetails, { - stubs: { GlButton, GlSprintf }, + stubs: { + GlButton, + GlSprintf, + GlDisclosureDropdown, + GlDisclosureDropdownItem, + GlDisclosureDropdownGroup, + }, store, mocks, propsData: { diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap index c8d972b19a3..9f2b91cb7fd 100644 --- a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap +++ b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap @@ -90,9 +90,11 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = </div> <div - class="js-vue-md-preview md md-preview-holder gl-px-5" + class="js-vue-md-preview md-preview-holder gl-px-5 md" style="display: none;" - /> + > + <div /> + </div> <!----> diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js index 21d085dc0fb..9714093e001 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js @@ -6,18 +6,21 @@ import CommandPaletteItems from '~/super_sidebar/components/global_search/comman import { COMMAND_HANDLE, USERS_GROUP_TITLE, + PATH_GROUP_TITLE, USER_HANDLE, + PATH_HANDLE, SEARCH_SCOPE, } from '~/super_sidebar/components/global_search/command_palette/constants'; import { commandMapper, linksReducer, + fileMapper, } from '~/super_sidebar/components/global_search/command_palette/utils'; import { getFormattedItem } from '~/super_sidebar/components/global_search/utils'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import waitForPromises from 'helpers/wait_for_promises'; -import { COMMANDS, LINKS, USERS } from './mock_data'; +import { COMMANDS, LINKS, USERS, FILES } from './mock_data'; const links = LINKS.reduce(linksReducer, []); @@ -25,6 +28,8 @@ describe('CommandPaletteItems', () => { let wrapper; const autocompletePath = '/autocomplete'; const searchContext = { project: { id: 1 }, group: { id: 2 } }; + const projectFilesPath = 'project/files/path'; + const projectBlobPath = '/blob/main'; const createComponent = (props) => { wrapper = shallowMount(CommandPaletteItems, { @@ -42,6 +47,8 @@ describe('CommandPaletteItems', () => { commandPaletteLinks: LINKS, autocompletePath, searchContext, + projectFilesPath, + projectBlobPath, }, }); }; @@ -50,7 +57,7 @@ describe('CommandPaletteItems', () => { const findGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup); const findLoader = () => wrapper.findComponent(GlLoadingIcon); - describe('COMMANDS & LINKS', () => { + describe('Commands and links', () => { it('renders all commands initially', () => { createComponent(); const commandGroup = COMMANDS.map(commandMapper)[0]; @@ -90,7 +97,7 @@ describe('CommandPaletteItems', () => { }); }); - describe('USERS, ISSUES, PROJECTS', () => { + describe('Users, issues, and projects', () => { let mockAxios; beforeEach(() => { @@ -140,4 +147,64 @@ describe('CommandPaletteItems', () => { expect(wrapper.text()).toBe('No results found'); }); }); + + describe('Project files', () => { + let mockAxios; + + beforeEach(() => { + mockAxios = new MockAdapter(axios); + }); + + it('should request project files on first search', () => { + jest.spyOn(axios, 'get'); + const searchQuery = 'gitlab-ci.yml'; + createComponent({ handle: PATH_HANDLE, searchQuery }); + + expect(axios.get).toHaveBeenCalledWith(projectFilesPath); + expect(findLoader().exists()).toBe(true); + }); + + it('should render returned items', async () => { + const items = FILES.map(fileMapper.bind(null, projectBlobPath)); + mockAxios.onGet().replyOnce(HTTP_STATUS_OK, FILES); + jest.spyOn(fuzzaldrinPlus, 'filter').mockReturnValue(items); + + const searchQuery = 'gitlab-ci.yml'; + createComponent({ handle: PATH_HANDLE, searchQuery }); + + await waitForPromises(); + + expect(findItems()).toHaveLength(items.length); + expect(findGroups().at(0).props('group')).toMatchObject({ + name: PATH_GROUP_TITLE, + items, + }); + }); + + it('should display no results message when no files matched the search query', async () => { + mockAxios.onGet().replyOnce(HTTP_STATUS_OK, []); + const searchQuery = 'gitlab-ci.yml'; + createComponent({ handle: PATH_HANDLE, searchQuery }); + await waitForPromises(); + expect(wrapper.text()).toBe('No results found'); + }); + + it('should not make additional server call on the search query change', async () => { + const searchQuery = 'gitlab-ci.yml'; + const newSearchQuery = 'package.json'; + + jest.spyOn(axios, 'get'); + + createComponent({ handle: PATH_HANDLE, searchQuery }); + + mockAxios.onGet().replyOnce(HTTP_STATUS_OK, FILES); + await waitForPromises(); + + expect(axios.get).toHaveBeenCalledTimes(1); + + await wrapper.setProps({ searchQuery: newSearchQuery }); + + expect(axios.get).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js index ec65a43d549..26a6501c338 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/mock_data.js @@ -131,3 +131,15 @@ export const ISSUE = { project_name: 'Flight', url: '/flightjs/Flight/-/issues/37', }; + +export const FILES = [ + '.codeclimate.yml', + '.gitignore', + '.gitlab-ci.yml', + '.gitlab/CODEOWNERS', + '.ruby-version', + '.tool-versions', + 'CHANGELOG', + 'CONTRIBUTING.md', + 'Dangerfile', +]; diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js index 0b75787723e..ebc52e2d910 100644 --- a/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/command_palette/utils_spec.js @@ -1,6 +1,7 @@ import { commandMapper, linksReducer, + fileMapper, } from '~/super_sidebar/components/global_search/command_palette/utils'; import { COMMANDS, LINKS, TRANSFORMED_LINKS } from './mock_data'; @@ -16,3 +17,15 @@ describe('commandMapper', () => { expect(COMMANDS.map(commandMapper)[0].items).toHaveLength(initialCommandsLength - 1); }); }); + +describe('fileMapper', () => { + it('should transform files', () => { + const file = 'file'; + const projectBlobPath = 'project/blob/path'; + expect(fileMapper(projectBlobPath, file)).toEqual({ + icon: 'doc-code', + text: file, + href: `${projectBlobPath}/${file}`, + }); + }); +}); diff --git a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js index 9b7b9e288df..cfbe508f3d0 100644 --- a/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js +++ b/spec/frontend/super_sidebar/components/global_search/components/global_search_spec.js @@ -12,6 +12,7 @@ import CommandPaletteItems from '~/super_sidebar/components/global_search/comman import { SEARCH_OR_COMMAND_MODE_PLACEHOLDER, COMMON_HANDLES, + PATH_HANDLE, } from '~/super_sidebar/components/global_search/command_palette/constants'; import { SEARCH_INPUT_DESCRIPTION, @@ -319,7 +320,7 @@ describe('GlobalSearchModal', () => { }); }); - describe.each(COMMON_HANDLES)( + describe.each([...COMMON_HANDLES, PATH_HANDLE])( 'when FF `command_palette` is enabled and search handle is %s', (handle) => { beforeEach(() => { @@ -415,7 +416,7 @@ describe('GlobalSearchModal', () => { describe('Modal events', () => { beforeEach(() => { - createComponent(); + createComponent({ search: 'searchQuery' }); }); it('should emit `shown` event when modal shown`', () => { @@ -423,9 +424,10 @@ describe('GlobalSearchModal', () => { expect(wrapper.emitted('shown')).toHaveLength(1); }); - it('should emit `hidden` event when modal hidden`', () => { - findGlobalSearchModal().vm.$emit('hidden'); + it('should emit `hidden` event when modal hidden and clear the search input', () => { + findGlobalSearchModal().vm.$emit('hide'); expect(wrapper.emitted('hidden')).toHaveLength(1); + expect(actionSpies.setSearch).toHaveBeenCalledWith(expect.any(Object), ''); }); }); }); diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index bdcf0ef57ee..9df044c8a66 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -421,6 +421,20 @@ RSpec.describe GroupsHelper do end end + describe '#can_admin_service_accounts?', feature_category: :user_management do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + + before do + allow(helper).to receive(:current_user) { user } + group.add_owner(user) + end + + it 'returns false when current_user can not admin members' do + expect(helper.can_admin_service_accounts?(group)).to be(false) + end + end + describe '#localized_jobs_to_be_done_choices' do it 'has a translation for all `jobs_to_be_done` values' do expect(localized_jobs_to_be_done_choices.keys).to match_array(NamespaceSetting.jobs_to_be_dones.keys) diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index 6648663b634..8f0bf156d1d 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -528,4 +528,31 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do expect(helper.super_sidebar_nav_panel(user: user)).to be_a(Sidebars::YourWork::Panel) end end + + describe '#command_palette_data' do + it 'returns data for project files search' do + project = create(:project, :repository) # rubocop:disable RSpec/FactoryBot/AvoidCreate + + expect(helper.command_palette_data(project: project)).to eq( + project_files_url: project_files_path( + project, project.default_branch, format: :json), + project_blob_url: project_blob_path( + project, project.default_branch) + ) + end + + it 'returns empty object when project is nil' do + expect(helper.command_palette_data(project: nil)).to eq({}) + end + + it 'returns empty object when project does not have repo' do + project = build(:project) + expect(helper.command_palette_data(project: project)).to eq({}) + end + + it 'returns empty object when project has repo but it is empty' do + project = build(:project, :empty_repo) + expect(helper.command_palette_data(project: project)).to eq({}) + end + end end diff --git a/spec/lib/api/entities/plan_limit_spec.rb b/spec/lib/api/entities/plan_limit_spec.rb index a2d183fd631..b0ad13995d7 100644 --- a/spec/lib/api/entities/plan_limit_spec.rb +++ b/spec/lib/api/entities/plan_limit_spec.rb @@ -20,6 +20,7 @@ RSpec.describe API::Entities::PlanLimit do :enforcement_limit, :generic_packages_max_file_size, :helm_max_file_size, + :limits_history, :maven_max_file_size, :notification_limit, :npm_max_file_size, diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 527ee96ca86..6a970b9939d 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1848,22 +1848,29 @@ RSpec.describe Group, feature_category: :groups_and_projects do end context 'user-related methods' do - let(:user_a) { create(:user) } - let(:user_b) { create(:user) } - let(:user_c) { create(:user) } - let(:user_d) { create(:user) } + let_it_be(:user_a) { create(:user) } + let_it_be(:user_b) { create(:user) } + let_it_be(:user_c) { create(:user) } + let_it_be(:user_d) { create(:user) } - let(:group) { create(:group) } - let(:nested_group) { create(:group, parent: group) } - let(:deep_nested_group) { create(:group, parent: nested_group) } - let(:project) { create(:project, namespace: group) } + let_it_be(:group) { create(:group) } + let_it_be(:nested_group) { create(:group, parent: group) } + let_it_be(:deep_nested_group) { create(:group, parent: nested_group) } + let_it_be(:project) { create(:project, namespace: group) } - before do + let_it_be(:another_group) { create(:group) } + let_it_be(:another_user) { create(:user) } + + before_all do group.add_developer(user_a) group.add_developer(user_c) nested_group.add_developer(user_b) deep_nested_group.add_developer(user_a) project.add_developer(user_d) + + another_group.add_developer(another_user) + + create(:group_group_link, shared_group: group, shared_with_group: another_group) end describe '#direct_and_indirect_users' do @@ -1876,6 +1883,13 @@ RSpec.describe Group, feature_category: :groups_and_projects do it 'does not return members of projects belonging to ancestor groups' do expect(nested_group.direct_and_indirect_users).not_to include(user_d) end + + context 'when share_with_groups is true' do + it 'also returns members of groups invited to this group' do + expect(group.direct_and_indirect_users(share_with_groups: true)) + .to contain_exactly(user_a, user_b, user_c, user_d, another_user) + end + end end describe '#direct_and_indirect_users_with_inactive' do diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index ed49009d6d9..19419db979a 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -246,6 +246,16 @@ RSpec.describe Integration, feature_category: :integrations do end end + describe '#ci?' do + it 'is true when integration is a CI integration' do + expect(build(:jenkins_integration).ci?).to eq(true) + end + + it 'is false when integration is not a ci integration' do + expect(build(:integration).ci?).to eq(false) + end + end + describe '.find_or_initialize_non_project_specific_integration' do let!(:integration_1) { create(:jira_integration, project_id: nil, group_id: group.id) } let!(:integration_2) { create(:jira_integration, project: project) } diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb index 265f781d61d..bee1c4f47b0 100644 --- a/spec/models/plan_limits_spec.rb +++ b/spec/models/plan_limits_spec.rb @@ -302,62 +302,132 @@ RSpec.describe PlanLimits do end end - describe '#log_limits_changes', :freeze_time do + describe '#format_limits_history', :freeze_time do let(:user) { create(:user) } let(:plan_limits) { create(:plan_limits) } let(:current_timestamp) { Time.current.utc.to_i } - let(:history) { plan_limits.limits_history } - it 'logs a single attribute change' do - plan_limits.log_limits_changes(user, enforcement_limit: 5_000) - - expect(history).to eq( - { 'enforcement_limit' => [{ 'user_id' => user.id, 'username' => user.username, - 'timestamp' => current_timestamp, 'value' => 5_000 }] } + it 'formats a single attribute change' do + formatted_limits_history = plan_limits.format_limits_history(user, enforcement_limit: 5_000) + + expect(formatted_limits_history).to eq( + { + "enforcement_limit" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 5000 + } + ] + } ) end - it 'logs multiple attribute changes' do - plan_limits.log_limits_changes(user, enforcement_limit: 10_000, notification_limit: 20_000) + it 'does not format limits_history for non-allowed attributes' do + formatted_limits_history = plan_limits.format_limits_history(user, + { enforcement_limit: 20_000, pipeline_hierarchy_size: 10_000 }) - expect(history).to eq( - { 'enforcement_limit' => [{ 'user_id' => user.id, 'username' => user.username, - 'timestamp' => current_timestamp, 'value' => 10_000 }], - 'notification_limit' => [{ 'user_id' => user.id, 'username' => user.username, - 'timestamp' => current_timestamp, - 'value' => 20_000 }] } - ) + expect(formatted_limits_history).to eq({ + "enforcement_limit" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 20_000 + } + ] + }) end - it 'allows logging dashboard_limit_enabled_at from console (without user)' do - plan_limits.log_limits_changes(nil, dashboard_limit_enabled_at: current_timestamp) + it 'does not format attributes for values that do not change' do + plan_limits.update!(enforcement_limit: 20_000) + formatted_limits_history = plan_limits.format_limits_history(user, enforcement_limit: 20_000) + + expect(formatted_limits_history).to eq({}) + end - expect(history).to eq( - { 'dashboard_limit_enabled_at' => [{ 'user_id' => nil, 'username' => nil, 'timestamp' => current_timestamp, - 'value' => current_timestamp }] } + it 'formats multiple attribute changes' do + formatted_limits_history = plan_limits.format_limits_history(user, enforcement_limit: 10_000, + notification_limit: 20_000, dashboard_limit_enabled_at: current_timestamp) + + expect(formatted_limits_history).to eq( + { + "notification_limit" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 20000 + } + ], + "enforcement_limit" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 10000 + } + ], + "dashboard_limit_enabled_at" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => current_timestamp + } + ] + } ) end - context 'with previous history avilable' do + context 'with previous history available' do let(:plan_limits) do - create(:plan_limits, - limits_history: { 'enforcement_limit' => [{ user_id: user.id, username: user.username, - timestamp: current_timestamp, - value: 20_000 }, - { user_id: user.id, username: user.username, timestamp: current_timestamp, - value: 50_000 }] }) + create( + :plan_limits, + limits_history: { + 'enforcement_limit' => [ + { + user_id: user.id, + username: user.username, + timestamp: current_timestamp, + value: 20_000 + }, + { + user_id: user.id, + username: user.username, + timestamp: current_timestamp, + value: 50_000 + } + ] + } + ) end it 'appends to it' do - plan_limits.log_limits_changes(user, enforcement_limit: 60_000) - expect(history).to eq( + formatted_limits_history = plan_limits.format_limits_history(user, enforcement_limit: 60_000) + + expect(formatted_limits_history).to eq( { - 'enforcement_limit' => [ - { 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp, - 'value' => 20_000 }, - { 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp, - 'value' => 50_000 }, - { 'user_id' => user.id, 'username' => user.username, 'timestamp' => current_timestamp, 'value' => 60_000 } + "enforcement_limit" => [ + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 20000 + }, + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 50000 + }, + { + "user_id" => user.id, + "username" => user.username, + "timestamp" => current_timestamp, + "value" => 60000 + } ] } ) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b5c47d9fc40..73489e6b35e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -6311,6 +6311,36 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr expect(recorder.count).to be_zero end + + context 'with a CI integration' do + let!(:ci_integration) do + create(:jenkins_integration, push_events: true, active: true, project: integration.project) + end + + it 'executes the integrations' do + [Integrations::Jenkins, Integrations::Slack].each do |integration_type| + expect_next_found_instance_of(integration_type) do |instance| + expect(instance).to receive(:async_execute).with('data').once + end + end + + integration.project.execute_integrations('data', :push_hooks) + end + + context 'and skipping ci' do + it 'does not execute ci integrations' do + expect_next_found_instance_of(Integrations::Jenkins) do |instance| + expect(instance).not_to receive(:async_execute) + end + + expect_next_found_instance_of(Integrations::Slack) do |instance| + expect(instance).to receive(:async_execute).with('data').once + end + + integration.project.execute_integrations('data', :push_hooks, skip_ci: true) + end + end + end end describe '#has_active_hooks?' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4fc4341673c..9709684f44b 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6182,6 +6182,29 @@ RSpec.describe User, feature_category: :user_profile do expect { user.delete_async(deleted_by: deleted_by) }.not_to change { user.note } end end + + describe '#allow_possible_spam?' do + context 'when no custom attribute is set' do + it 'is false' do + expect(user.allow_possible_spam?).to be_falsey + end + end + + context 'when the custom attribute is set' do + before do + user.custom_attributes.upsert_custom_attributes( + [{ + user_id: user.id, + key: UserCustomAttribute::ALLOW_POSSIBLE_SPAM, + value: "test" + }]) + end + + it '#allow_possible_spam? is true' do + expect(user.allow_possible_spam?).to be_truthy + end + end + end end end diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb index cad1111b76b..97eb8a2b13f 100644 --- a/spec/requests/api/admin/plan_limits_spec.rb +++ b/spec/requests/api/admin/plan_limits_spec.rb @@ -26,6 +26,7 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size) expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size) expect(json_response['helm_max_file_size']).to eq(Plan.default.actual_limits.helm_max_file_size) + expect(json_response['limits_history']).to eq(Plan.default.actual_limits.limits_history) expect(json_response['maven_max_file_size']).to eq(Plan.default.actual_limits.maven_max_file_size) expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size) expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size) @@ -86,7 +87,9 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d let(:params) { { 'plan_name': 'default' } } end - context 'as an admin user' do + context 'as an admin user', :freeze_time do + let(:current_timestamp) { Time.current.utc.to_i } + context 'correct params' do it 'updates multiple plan limits', :aggregate_failures do put api(path, admin, admin_mode: true), params: { @@ -124,6 +127,11 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d expect(json_response['enforcement_limit']).to eq(15) expect(json_response['generic_packages_max_file_size']).to eq(20) expect(json_response['helm_max_file_size']).to eq(25) + expect(json_response['limits_history']).to eq( + { "enforcement_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 15 }], + "notification_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 90 }], + "storage_size_limit" => [{ "user_id" => admin.id, "username" => admin.username, "timestamp" => current_timestamp, "value" => 80 }] } + ) expect(json_response['maven_max_file_size']).to eq(30) expect(json_response['notification_limit']).to eq(90) expect(json_response['npm_max_file_size']).to eq(40) diff --git a/spec/serializers/diff_viewer_entity_spec.rb b/spec/serializers/diff_viewer_entity_spec.rb index 84d2bdceb78..7ee2f8ec12f 100644 --- a/spec/serializers/diff_viewer_entity_spec.rb +++ b/spec/serializers/diff_viewer_entity_spec.rb @@ -16,47 +16,27 @@ RSpec.describe DiffViewerEntity do subject { described_class.new(viewer).as_json(options) } - context 'when add_ignore_all_white_spaces is enabled' do - before do - stub_feature_flags(add_ignore_all_white_spaces: true) - end - - it 'serializes diff file viewer' do - expect(subject.with_indifferent_access).to match_schema('entities/diff_viewer') - end - - it 'contains whitespace_only attribute' do - expect(subject.with_indifferent_access).to include(:whitespace_only) - end - - context 'when whitespace_only option is true' do - let(:options) { { whitespace_only: true } } + it 'serializes diff file viewer' do + expect(subject.with_indifferent_access).to match_schema('entities/diff_viewer') + end - it 'returns the whitespace_only attribute true' do - expect(subject.with_indifferent_access[:whitespace_only]).to eq true - end - end + it 'contains whitespace_only attribute' do + expect(subject.with_indifferent_access).to include(:whitespace_only) + end - context 'when whitespace_only option is false' do - let(:options) { { whitespace_only: false } } + context 'when whitespace_only option is true' do + let(:options) { { whitespace_only: true } } - it 'returns the whitespace_only attribute false' do - expect(subject.with_indifferent_access[:whitespace_only]).to eq false - end + it 'returns the whitespace_only attribute true' do + expect(subject.with_indifferent_access[:whitespace_only]).to eq true end end - context 'when add_ignore_all_white_spaces is disabled ' do - before do - stub_feature_flags(add_ignore_all_white_spaces: false) - end - - it 'serializes diff file viewer' do - expect(subject.with_indifferent_access).to match_schema('entities/diff_viewer') - end + context 'when whitespace_only option is false' do + let(:options) { { whitespace_only: false } } - it 'does not contain whitespace_only attribute' do - expect(subject.with_indifferent_access).not_to include(:whitespace_only) + it 'returns the whitespace_only attribute false' do + expect(subject.with_indifferent_access[:whitespace_only]).to eq false end end end diff --git a/spec/services/admin/plan_limits/update_service_spec.rb b/spec/services/admin/plan_limits/update_service_spec.rb index 4a384b98299..718367fadc2 100644 --- a/spec/services/admin/plan_limits/update_service_spec.rb +++ b/spec/services/admin/plan_limits/update_service_spec.rb @@ -33,12 +33,10 @@ RSpec.describe Admin::PlanLimits::UpdateService, feature_category: :shared do subject(:update_plan_limits) { described_class.new(params, current_user: user, plan: plan).execute } context 'when current_user is an admin', :enable_admin_mode do - context 'when the update is successful' do - it 'updates all attributes' do - expect_next_instance_of(described_class) do |instance| - expect(instance).to receive(:parsed_params).and_call_original - end + context 'when the update is successful', :freeze_time do + let(:current_timestamp) { Time.current.utc.to_i } + it 'updates all attributes' do update_plan_limits params.each do |key, value| @@ -46,6 +44,22 @@ RSpec.describe Admin::PlanLimits::UpdateService, feature_category: :shared do end end + it 'logs the allowed attributes only' do + update_plan_limits + + expect(limits.limits_history).to eq( + { "enforcement_limit" => + [{ "user_id" => user.id, "username" => user.username, + "timestamp" => current_timestamp, "value" => 15 }], + "notification_limit" => + [{ "user_id" => user.id, "username" => user.username, + "timestamp" => current_timestamp, "value" => 30 }], + "storage_size_limit" => + [{ "user_id" => user.id, "username" => user.username, + "timestamp" => current_timestamp, "value" => 90 }] } + ) + end + it 'returns success' do response = update_plan_limits diff --git a/spec/services/git/base_hooks_service_spec.rb b/spec/services/git/base_hooks_service_spec.rb index 8a686a19c4c..60883db0cd5 100644 --- a/spec/services/git/base_hooks_service_spec.rb +++ b/spec/services/git/base_hooks_service_spec.rb @@ -102,10 +102,25 @@ RSpec.describe Git::BaseHooksService, feature_category: :source_code_management it 'executes the services' do expect(subject).to receive(:push_data).at_least(:once).and_call_original - expect(project).to receive(:execute_integrations) + expect(project).to receive(:execute_integrations).with(kind_of(Hash), subject.hook_name, skip_ci: false) subject.execute end + + context 'with integrations.skip_ci push option' do + before do + params[:push_options] = { + integrations: { skip_ci: true } + } + end + + it 'executes the services' do + expect(subject).to receive(:push_data).at_least(:once).and_call_original + expect(project).to receive(:execute_integrations).with(kind_of(Hash), subject.hook_name, skip_ci: true) + + subject.execute + end + end end context 'with inactive integrations' do diff --git a/spec/services/groups/participants_service_spec.rb b/spec/services/groups/participants_service_spec.rb index 749b3e9e3fd..d30ac5ac1a1 100644 --- a/spec/services/groups/participants_service_spec.rb +++ b/spec/services/groups/participants_service_spec.rb @@ -4,19 +4,21 @@ require 'spec_helper' RSpec.describe Groups::ParticipantsService, feature_category: :groups_and_projects do describe '#execute' do - let(:user) { create(:user) } - let(:parent_group) { create(:group) } - let(:group) { create(:group, parent: parent_group) } - let(:subgroup) { create(:group, parent: group) } - let(:subproject) { create(:project, group: subgroup) } + let_it_be(:developer) { create(:user) } + let_it_be(:parent_group) { create(:group) } + let_it_be(:group) { create(:group, parent: parent_group) } + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:subproject) { create(:project, group: subgroup) } - subject(:service_result) { described_class.new(group, user).execute(nil) } + let(:service) { described_class.new(group, developer) } - before do - parent_group.add_developer(create(:user)) - subgroup.add_developer(create(:user)) - subproject.add_developer(create(:user)) + subject(:service_result) { service.execute(nil) } + before_all do + parent_group.add_developer(developer) + end + + before do stub_feature_flags(disable_all_mention: false) end @@ -24,21 +26,48 @@ RSpec.describe Groups::ParticipantsService, feature_category: :groups_and_projec expect(service_result).to include(a_hash_including({ username: "all", name: "All Group Members" })) end + context 'when `disable_all_mention` FF is enabled' do + before do + stub_feature_flags(disable_all_mention: true) + end + + it 'does not include `All Group Members`' do + expect(service_result).not_to include(a_hash_including({ username: "all", name: "All Group Members" })) + end + end + it 'returns all members in parent groups, sub-groups, and sub-projects' do + parent_group.add_developer(create(:user)) + subgroup.add_developer(create(:user)) + subproject.add_developer(create(:user)) + expected_users = (group.self_and_hierarchy.flat_map(&:users) + subproject.users) .map { |user| user_to_autocompletable(user) } - expect(expected_users.count).to eq(3) + expect(expected_users.count).to eq(4) expect(service_result).to include(*expected_users) end - context 'when `disable_all_mention` FF is enabled' do - before do - stub_feature_flags(disable_all_mention: true) + context 'when shared with a private group' do + let_it_be(:private_group_member) { create(:user) } + let_it_be(:private_group) { create(:group, :private, :nested) } + + before_all do + private_group.add_owner(private_group_member) + + create(:group_group_link, shared_group: parent_group, shared_with_group: private_group) end - it 'does not include `All Group Members`' do - expect(service_result).not_to include(a_hash_including({ username: "all", name: "All Group Members" })) + subject(:usernames) { service_result.pluck(:username) } + + context 'when current_user is not a member' do + let(:service) { described_class.new(group, create(:user)) } + + it { is_expected.not_to include(private_group_member.username) } + end + + context 'when current_user is a member' do + it { is_expected.to include(private_group_member.username) } end end end diff --git a/spec/services/spam/spam_verdict_service_spec.rb b/spec/services/spam/spam_verdict_service_spec.rb index 5baa476a23e..bf627304807 100644 --- a/spec/services/spam/spam_verdict_service_spec.rb +++ b/spec/services/spam/spam_verdict_service_spec.rb @@ -136,6 +136,38 @@ RSpec.describe Spam::SpamVerdictService, feature_category: :instance_resiliency end end + context 'if allow_possible_spam user custom attribute is set' do + before do + UserCustomAttribute.upsert_custom_attributes( + [{ + user_id: user.id, + key: 'allow_possible_spam', + value: 'does not matter' + }] + ) + end + + context 'and a service returns a verdict that should be overridden' do + before do + allow(service).to receive(:get_spamcheck_verdict).and_return(BLOCK_USER) + end + + it 'overrides and renders the override verdict' do + is_expected.to eq OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM + end + end + + context 'and a service returns a verdict that does not need to be overridden' do + before do + allow(service).to receive(:get_spamcheck_verdict).and_return(ALLOW) + end + + it 'does not override and renders the original verdict' do + is_expected.to eq ALLOW + end + end + end + context 'records metrics' do let(:histogram) { instance_double(Prometheus::Client::Histogram) } diff --git a/spec/services/users/allow_possible_spam_service_spec.rb b/spec/services/users/allow_possible_spam_service_spec.rb new file mode 100644 index 00000000000..53618f0c8e9 --- /dev/null +++ b/spec/services/users/allow_possible_spam_service_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::AllowPossibleSpamService, feature_category: :user_management do + let_it_be(:current_user) { create(:admin) } + + subject(:service) { described_class.new(current_user) } + + describe '#execute' do + let(:user) { create(:user) } + + subject(:operation) { service.execute(user) } + + it 'updates the custom attributes', :aggregate_failures do + expect(user.custom_attributes).to be_empty + + operation + user.reload + + expect(user.custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM)).to be_present + end + end +end diff --git a/spec/services/users/disallow_possible_spam_service_spec.rb b/spec/services/users/disallow_possible_spam_service_spec.rb new file mode 100644 index 00000000000..32a47e05525 --- /dev/null +++ b/spec/services/users/disallow_possible_spam_service_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Users::DisallowPossibleSpamService, feature_category: :user_management do + let_it_be(:current_user) { create(:admin) } + + subject(:service) { described_class.new(current_user) } + + describe '#execute' do + let(:user) { create(:user) } + + subject(:operation) { service.execute(user) } + + before do + UserCustomAttribute.upsert_custom_attributes( + [{ + user_id: user.id, + key: :allow_possible_spam, + value: 'not important' + }] + ) + end + + it 'updates the custom attributes', :aggregate_failures do + expect(user.custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM)).to be_present + + operation + user.reload + + expect(user.custom_attributes).to be_empty + end + end +end diff --git a/spec/support/helpers/features/autocomplete_helpers.rb b/spec/support/helpers/features/autocomplete_helpers.rb new file mode 100644 index 00000000000..106e823afef --- /dev/null +++ b/spec/support/helpers/features/autocomplete_helpers.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Features + module AutocompleteHelpers + def find_autocomplete_menu + find('.atwho-view ul', visible: true) + end + + def find_highlighted_autocomplete_item + find('.atwho-view li.cur', visible: true) + end + end +end |