diff options
Diffstat (limited to 'spec')
14 files changed, 348 insertions, 41 deletions
diff --git a/spec/experiments/ios_specific_templates_experiment_spec.rb b/spec/experiments/ios_specific_templates_experiment_spec.rb new file mode 100644 index 00000000000..4d02381dbde --- /dev/null +++ b/spec/experiments/ios_specific_templates_experiment_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IosSpecificTemplatesExperiment do + subject do + described_class.new(actor: user, project: project) do |e| + e.candidate { true } + end.run + end + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :auto_devops_disabled) } + + let!(:project_setting) { create(:project_setting, project: project, target_platforms: target_platforms) } + let(:target_platforms) { %w(ios) } + + before do + stub_experiments(ios_specific_templates: :candidate) + project.add_developer(user) if user + end + + it { is_expected.to be true } + + describe 'skipping the experiment' do + context 'no actor' do + let_it_be(:user) { nil } + + it { is_expected.to be_falsey } + end + + context 'actor cannot create pipelines' do + before do + project.add_guest(user) + end + + it { is_expected.to be_falsey } + end + + context 'targeting a non iOS platform' do + let(:target_platforms) { [] } + + it { is_expected.to be_falsey } + end + + context 'project has a ci.yaml file' do + before do + allow(project).to receive(:has_ci?).and_return(true) + end + + it { is_expected.to be_falsey } + end + + context 'project has pipelines' do + before do + create(:ci_pipeline, project: project) + end + + it { is_expected.to be_falsey } + end + end +end diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 6643ebe82e6..15bc2318022 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -36,14 +36,14 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js do click_on "1" # Scopes - check "api" + check "read_api" check "read_user" click_on "Create impersonation token" expect(active_impersonation_tokens).to have_text(name) expect(active_impersonation_tokens).to have_text('in') - expect(active_impersonation_tokens).to have_text('api') + expect(active_impersonation_tokens).to have_text('read_api') expect(active_impersonation_tokens).to have_text('read_user') expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) expect(created_impersonation_token).not_to be_empty diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index f1e5658cd7b..8cbc0491441 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -47,14 +47,14 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do click_on "1" # Scopes - check "api" + check "read_api" check "read_user" click_on "Create personal access token" expect(active_personal_access_tokens).to have_text(name) expect(active_personal_access_tokens).to have_text('in') - expect(active_personal_access_tokens).to have_text('api') + expect(active_personal_access_tokens).to have_text('read_api') expect(active_personal_access_tokens).to have_text('read_user') expect(created_personal_access_token).not_to be_empty end diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb index bf40aea4a5d..271dce44db7 100644 --- a/spec/finders/users_finder_spec.rb +++ b/spec/finders/users_finder_spec.rb @@ -14,7 +14,7 @@ RSpec.describe UsersFinder do it 'returns searchable users' do users = described_class.new(user).execute - expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, internal_user, admin_user, project_bot) + expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'filters by username' do @@ -56,7 +56,7 @@ RSpec.describe UsersFinder do it 'filters by non external users' do users = described_class.new(user, non_external: true).execute - expect(users).to contain_exactly(user, normal_user, omniauth_user, internal_user, admin_user, project_bot) + expect(users).to contain_exactly(user, normal_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'filters by created_at' do @@ -73,7 +73,7 @@ RSpec.describe UsersFinder do it 'filters by non internal users' do users = described_class.new(user, non_internal: true).execute - expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, admin_user, project_bot) + expect(users).to contain_exactly(user, normal_user, unconfirmed_user, external_user, omniauth_user, admin_user, project_bot) end it 'does not filter by custom attributes' do @@ -82,18 +82,18 @@ RSpec.describe UsersFinder do custom_attributes: { foo: 'bar' } ).execute - expect(users).to contain_exactly(user, normal_user, external_user, omniauth_user, internal_user, admin_user, project_bot) + expect(users).to contain_exactly(user, normal_user, external_user, unconfirmed_user, omniauth_user, internal_user, admin_user, project_bot) end it 'orders returned results' do users = described_class.new(user, sort: 'id_asc').execute - expect(users).to eq([normal_user, admin_user, external_user, omniauth_user, internal_user, project_bot, user]) + expect(users).to eq([normal_user, admin_user, external_user, unconfirmed_user, omniauth_user, internal_user, project_bot, user]) end it 'does not filter by admins' do users = described_class.new(user, admins: true).execute - expect(users).to contain_exactly(user, normal_user, external_user, admin_user, omniauth_user, internal_user, project_bot) + expect(users).to contain_exactly(user, normal_user, external_user, admin_user, unconfirmed_user, omniauth_user, internal_user, project_bot) end end diff --git a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js index b6be53fec99..7952661e2d2 100644 --- a/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js +++ b/spec/frontend/header_search/components/header_search_autocomplete_items_spec.js @@ -8,6 +8,9 @@ import { LARGE_AVATAR_PX, PROJECTS_CATEGORY, SMALL_AVATAR_PX, + ISSUES_CATEGORY, + MERGE_REQUEST_CATEGORY, + RECENT_EPICS_CATEGORY, } from '~/header_search/constants'; import { MOCK_GROUPED_AUTOCOMPLETE_OPTIONS, @@ -50,7 +53,12 @@ describe('HeaderSearchAutocompleteItems', () => { const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); const findGlDropdownDividers = () => wrapper.findAllComponents(GlDropdownDivider); const findFirstDropdownItem = () => findDropdownItems().at(0); - const findDropdownItemTitles = () => findDropdownItems().wrappers.map((w) => w.text()); + const findDropdownItemTitles = () => + findDropdownItems().wrappers.map((w) => w.findAll('span').at(1).text()); + const findDropdownItemSubTitles = () => + findDropdownItems() + .wrappers.filter((w) => w.findAll('span').length > 2) + .map((w) => w.findAll('span').at(2).text()); const findDropdownItemLinks = () => findDropdownItems().wrappers.map((w) => w.attributes('href')); const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findGlAvatar = () => wrapper.findComponent(GlAvatar); @@ -95,10 +103,17 @@ describe('HeaderSearchAutocompleteItems', () => { }); it('renders titles correctly', () => { - const expectedTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.label); + const expectedTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.value || o.label); expect(findDropdownItemTitles()).toStrictEqual(expectedTitles); }); + it('renders sub-titles correctly', () => { + const expectedSubTitles = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.filter((o) => o.value).map( + (o) => o.label, + ); + expect(findDropdownItemSubTitles()).toStrictEqual(expectedSubTitles); + }); + it('renders links correctly', () => { const expectedLinks = MOCK_SORTED_AUTOCOMPLETE_OPTIONS.map((o) => o.url); expect(findDropdownItemLinks()).toStrictEqual(expectedLinks); @@ -106,15 +121,30 @@ describe('HeaderSearchAutocompleteItems', () => { }); describe.each` - item | showAvatar | avatarSize - ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} - ${{ data: [{ category: GROUPS_CATEGORY, avatar_url: '/123' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} - ${{ data: [{ category: 'Help', avatar_url: '' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} - ${{ data: [{ category: 'Settings' }] }} | ${false} | ${false} - `('GlAvatar', ({ item, showAvatar, avatarSize }) => { + item | showAvatar | avatarSize | searchContext | entityId | entityName + ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 29 } }} | ${'29'} | ${''} + ${{ data: [{ category: GROUPS_CATEGORY, avatar_url: '/123' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 12 } }} | ${'12'} | ${''} + ${{ data: [{ category: 'Help', avatar_url: '' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'0'} | ${''} + ${{ data: [{ category: 'Settings' }] }} | ${false} | ${false} | ${null} | ${false} | ${false} + ${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 1, name: 'test1' } }} | ${'1'} | ${'test1'} + ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 2, name: 'test2' } }} | ${'2'} | ${'test2'} + ${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 3, name: 'test3' } }} | ${'3'} | ${'test3'} + ${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 4, name: 'test4' } }} | ${'4'} | ${'test4'} + ${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ group: { id: 5, name: 'test5' } }} | ${'5'} | ${'test5'} + ${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null, group_id: 6, group_name: 'test6' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${null} | ${'6'} | ${'test6'} + ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null, project_id: 7, project_name: 'test7' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${null} | ${'7'} | ${'test7'} + ${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null, project_id: 8, project_name: 'test8' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'8'} | ${'test8'} + ${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null, project_id: 9, project_name: 'test9' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'9'} | ${'test9'} + ${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null, group_id: 10, group_name: 'test10' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${null} | ${'10'} | ${'test10'} + ${{ data: [{ category: GROUPS_CATEGORY, avatar_url: null, group_id: 11, group_name: 'test11' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ group: { id: 1, name: 'test1' } }} | ${'11'} | ${'test11'} + ${{ data: [{ category: PROJECTS_CATEGORY, avatar_url: null, project_id: 12, project_name: 'test12' }] }} | ${true} | ${String(LARGE_AVATAR_PX)} | ${{ project: { id: 2, name: 'test2' } }} | ${'12'} | ${'test12'} + ${{ data: [{ category: ISSUES_CATEGORY, avatar_url: null, project_id: 13, project_name: 'test13' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 3, name: 'test3' } }} | ${'13'} | ${'test13'} + ${{ data: [{ category: MERGE_REQUEST_CATEGORY, avatar_url: null, project_id: 14, project_name: 'test14' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ project: { id: 4, name: 'test4' } }} | ${'14'} | ${'test14'} + ${{ data: [{ category: RECENT_EPICS_CATEGORY, avatar_url: null, group_id: 15, group_name: 'test15' }] }} | ${true} | ${String(SMALL_AVATAR_PX)} | ${{ group: { id: 5, name: 'test5' } }} | ${'15'} | ${'test15'} + `('GlAvatar', ({ item, showAvatar, avatarSize, searchContext, entityId, entityName }) => { describe(`when category is ${item.data[0].category} and avatar_url is ${item.data[0].avatar_url}`, () => { beforeEach(() => { - createComponent({}, { autocompleteGroupedSearchOptions: () => [item] }); + createComponent({ searchContext }, { autocompleteGroupedSearchOptions: () => [item] }); }); it(`should${showAvatar ? '' : ' not'} render`, () => { @@ -124,6 +154,16 @@ describe('HeaderSearchAutocompleteItems', () => { it(`should set avatarSize to ${avatarSize}`, () => { expect(findGlAvatar().exists() && findGlAvatar().attributes('size')).toBe(avatarSize); }); + + it(`should set avatar entityId to ${entityId}`, () => { + expect(findGlAvatar().exists() && findGlAvatar().attributes('entityid')).toBe(entityId); + }); + + it(`should set avatar entityName to ${entityName}`, () => { + expect(findGlAvatar().exists() && findGlAvatar().attributes('entityname')).toBe( + entityName, + ); + }); }); }); }); diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js index 358c224dfa6..b6f0fdcc29d 100644 --- a/spec/frontend/header_search/mock_data.js +++ b/spec/frontend/header_search/mock_data.js @@ -96,19 +96,22 @@ export const MOCK_AUTOCOMPLETE_OPTIONS_RES = [ { category: 'Projects', id: 1, - label: 'MockProject1', + label: 'Gitlab Org / MockProject1', + value: 'MockProject1', url: 'project/1', }, { category: 'Groups', id: 1, - label: 'MockGroup1', + label: 'Gitlab Org / MockGroup1', + value: 'MockGroup1', url: 'group/1', }, { category: 'Projects', id: 2, - label: 'MockProject2', + label: 'Gitlab Org / MockProject2', + value: 'MockProject2', url: 'project/2', }, { @@ -123,21 +126,24 @@ export const MOCK_AUTOCOMPLETE_OPTIONS = [ category: 'Projects', html_id: 'autocomplete-Projects-0', id: 1, - label: 'MockProject1', + label: 'Gitlab Org / MockProject1', + value: 'MockProject1', url: 'project/1', }, { category: 'Groups', html_id: 'autocomplete-Groups-1', id: 1, - label: 'MockGroup1', + label: 'Gitlab Org / MockGroup1', + value: 'MockGroup1', url: 'group/1', }, { category: 'Projects', html_id: 'autocomplete-Projects-2', id: 2, - label: 'MockProject2', + label: 'Gitlab Org / MockProject2', + value: 'MockProject2', url: 'project/2', }, { @@ -157,7 +163,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [ html_id: 'autocomplete-Projects-0', id: 1, - label: 'MockProject1', + label: 'Gitlab Org / MockProject1', + value: 'MockProject1', url: 'project/1', }, { @@ -165,7 +172,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [ html_id: 'autocomplete-Projects-2', id: 2, - label: 'MockProject2', + label: 'Gitlab Org / MockProject2', + value: 'MockProject2', url: 'project/2', }, ], @@ -178,7 +186,8 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [ html_id: 'autocomplete-Groups-1', id: 1, - label: 'MockGroup1', + label: 'Gitlab Org / MockGroup1', + value: 'MockGroup1', url: 'group/1', }, ], @@ -202,21 +211,24 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [ category: 'Projects', html_id: 'autocomplete-Projects-0', id: 1, - label: 'MockProject1', + label: 'Gitlab Org / MockProject1', + value: 'MockProject1', url: 'project/1', }, { category: 'Projects', html_id: 'autocomplete-Projects-2', id: 2, - label: 'MockProject2', + label: 'Gitlab Org / MockProject2', + value: 'MockProject2', url: 'project/2', }, { category: 'Groups', html_id: 'autocomplete-Groups-1', id: 1, - label: 'MockGroup1', + label: 'Gitlab Org / MockGroup1', + value: 'MockGroup1', url: 'group/1', }, { diff --git a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js index b0d5859cd31..3d7bf7acb41 100644 --- a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js +++ b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item_spec.js @@ -72,7 +72,7 @@ describe('GroupsListItem', () => { expect(addSubscriptionSpy).toHaveBeenCalledWith(mockSubscriptionPath, mockGroup1.full_path); expect(persistAlert).toHaveBeenCalledWith({ - linkUrl: '/help/integration/jira_development_panel.html#usage', + linkUrl: '/help/integration/jira_development_panel.html#use-the-integration', message: 'You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}', title: 'Namespace successfully linked', diff --git a/spec/helpers/ci/pipelines_helper_spec.rb b/spec/helpers/ci/pipelines_helper_spec.rb index 2b76eaa87bc..c473e1e4ab6 100644 --- a/spec/helpers/ci/pipelines_helper_spec.rb +++ b/spec/helpers/ci/pipelines_helper_spec.rb @@ -151,5 +151,46 @@ RSpec.describe Ci::PipelinesHelper do end end end + + describe 'the `registration_token` attribute' do + subject { data[:registration_token] } + + describe 'when the project is eligible for the `ios_specific_templates` experiment' do + let_it_be(:project) { create(:project, :auto_devops_disabled) } + let_it_be(:user) { create(:user) } + + before do + allow(helper).to receive(:current_user).and_return(user) + project.add_developer(user) + create(:project_setting, project: project, target_platforms: %w(ios)) + end + + context 'when the `ios_specific_templates` experiment variant is control' do + before do + stub_experiments(ios_specific_templates: :control) + end + + it { is_expected.to be_nil } + end + + context 'when the `ios_specific_templates` experiment variant is candidate' do + before do + stub_experiments(ios_specific_templates: :candidate) + end + + context 'when the user cannot register project runners' do + before do + allow(helper).to receive(:can?).with(user, :register_project_runners, project).and_return(false) + end + + it { is_expected.to be_nil } + end + + context 'when the user can register project runners' do + it { is_expected.to eq(project.runners_token) } + end + end + end + end end end diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 96d8157dfde..d1be451a759 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -71,7 +71,7 @@ RSpec.describe SearchHelper do create(:group).add_owner(user) result = search_autocomplete_opts("gro").first - expect(result.keys).to match_array(%i[category id label url avatar_url]) + expect(result.keys).to match_array(%i[category id value label url avatar_url]) end it 'includes the users recently viewed issues', :aggregate_failures do diff --git a/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb new file mode 100644 index 00000000000..fbd5fe546fa --- /dev/null +++ b/spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +require 'spec_helper' +require_migration! + +RSpec.describe UpdatePagesOnboardingState do + let(:migration) { described_class.new } + let!(:namespaces) { table(:namespaces) } + let!(:projects) { table(:projects) } + let!(:project_pages_metadata) { table(:project_pages_metadata) } + + let!(:namespace1) { namespaces.create!(name: 'foo', path: 'foo') } + let!(:namespace2) { namespaces.create!(name: 'bar', path: 'bar') } + let!(:project1) { projects.create!(namespace_id: namespace1.id) } + let!(:project2) { projects.create!(namespace_id: namespace2.id) } + let!(:pages_metadata1) do + project_pages_metadata.create!( + project_id: project1.id, + deployed: true, + onboarding_complete: false + ) + end + + let!(:pages_metadata2) do + project_pages_metadata.create!( + project_id: project2.id, + deployed: false, + onboarding_complete: false + ) + end + + describe '#up' do + before do + migration.up + end + + it 'sets the onboarding_complete attribute to the value of deployed' do + expect(pages_metadata1.reload.onboarding_complete).to eq(true) + expect(pages_metadata2.reload.onboarding_complete).to eq(false) + end + end + + describe '#down' do + before do + migration.up + migration.down + end + + it 'sets all onboarding_complete attributes to false' do + expect(pages_metadata1.reload.onboarding_complete).to eq(false) + expect(pages_metadata2.reload.onboarding_complete).to eq(false) + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ead7f7d0786..43088739f34 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2293,6 +2293,44 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#pages_show_onboarding?' do + let(:project) { create(:project) } + + subject { project.pages_show_onboarding? } + + context "if there is no metadata" do + it { is_expected.to be_truthy } + end + + context 'if onboarding is complete' do + before do + project.pages_metadatum.update_column(:onboarding_complete, true) + end + + it { is_expected.to be_falsey } + end + + context 'if there is metadata, but onboarding is not complete' do + before do + project.pages_metadatum.update_column(:onboarding_complete, false) + end + + it { is_expected.to be_truthy } + end + + # During migration, the onboarding_complete property can still be false, + # but will be updated later. To account for that case, pages_show_onboarding? + # should return false if `deployed` is true. + context "will return false if pages is deployed even if onboarding_complete is false" do + before do + project.pages_metadatum.update_column(:onboarding_complete, false) + project.pages_metadatum.update_column(:deployed, true) + end + + it { is_expected.to be_falsey } + end + end + describe '#pages_deployed?' do let(:project) { create(:project) } @@ -6626,6 +6664,25 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#mark_pages_onboarding_complete' do + let(:project) { create(:project) } + + it "creates new record and sets onboarding_complete to true if none exists yet" do + project.mark_pages_onboarding_complete + + expect(project.pages_metadatum.reload.onboarding_complete).to eq(true) + end + + it "overrides an existing setting" do + pages_metadatum = project.pages_metadatum + pages_metadatum.update!(onboarding_complete: false) + + expect do + project.mark_pages_onboarding_complete + end.to change { pages_metadatum.reload.onboarding_complete }.from(false).to(true) + end + end + describe '#mark_pages_as_deployed' do let(:project) { create(:project) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 5f2777b12d2..09e3a9c1156 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4988,17 +4988,36 @@ RSpec.describe User do end describe '#attention_requested_open_merge_requests_count' do - it 'returns number of open merge requests from non-archived projects' do - user = create(:user) - project = create(:project, :public) - archived_project = create(:project, :public, :archived) + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let(:archived_project) { create(:project, :public, :archived) } + before do create(:merge_request, source_project: project, author: user, reviewers: [user]) create(:merge_request, :closed, source_project: project, author: user, reviewers: [user]) create(:merge_request, source_project: archived_project, author: user, reviewers: [user]) + end + it 'returns number of open merge requests from non-archived projects' do + expect(Rails.cache).not_to receive(:fetch) expect(user.attention_requested_open_merge_requests_count(force: true)).to eq 1 end + + context 'when uncached_mr_attention_requests_count is disabled' do + before do + stub_feature_flags(uncached_mr_attention_requests_count: false) + end + + it 'fetches from cache' do + expect(Rails.cache).to receive(:fetch).with( + user.attention_request_cache_key, + force: false, + expires_in: described_class::COUNT_CACHE_VALIDITY_PERIOD + ).and_call_original + + expect(user.attention_requested_open_merge_requests_count).to eq 1 + end + end end describe '#assigned_open_issues_count' do @@ -6748,9 +6767,9 @@ RSpec.describe User do let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } let_it_be(:internal_user) { User.alert_bot.tap { |u| u.confirm } } - it 'does not return blocked, banned or unconfirmed users' do + it 'does not return blocked or banned users' do expect(described_class.without_forbidden_states).to match_array([ - normal_user, admin_user, external_user, omniauth_user, internal_user + normal_user, admin_user, external_user, unconfirmed_user, omniauth_user, internal_user ]) end end diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index 2bc31153f2c..07efd56fef4 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -260,6 +260,29 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.') end end + + context 'applies correct scope when throttling' do + before do + stub_application_setting(project_download_export_limit: 1) + end + + it 'throttles downloads within same namespaces' do + # simulate prior request to the same namespace, which increments the rate limit counter for that scope + Gitlab::ApplicationRateLimiter.throttled?(:project_download_export, scope: [user, project_finished.namespace]) + + get api(download_path_finished, user) + expect(response).to have_gitlab_http_status(:too_many_requests) + end + + it 'allows downloads from different namespaces' do + # simulate prior request to a different namespace, which increments the rate limit counter for that scope + Gitlab::ApplicationRateLimiter.throttled?(:project_download_export, + scope: [user, create(:project, :with_export).namespace]) + + get api(download_path_finished, user) + expect(response).to have_gitlab_http_status(:ok) + end + end end context 'when user is a maintainer' do diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb index ae246a87bb6..215d9d3e5a8 100644 --- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb +++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb @@ -29,15 +29,15 @@ RSpec.shared_examples 'resource access tokens creation' do |resource_type| click_on '1' # Scopes - check 'api' check 'read_api' + check 'read_repository' click_on "Create #{resource_type} access token" expect(active_resource_access_tokens).to have_text(name) expect(active_resource_access_tokens).to have_text('in') - expect(active_resource_access_tokens).to have_text('api') expect(active_resource_access_tokens).to have_text('read_api') + expect(active_resource_access_tokens).to have_text('read_repository') expect(active_resource_access_tokens).to have_text('Maintainer') expect(created_resource_access_token).not_to be_empty end |