diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-14 21:08:55 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-14 21:08:55 +0300 |
commit | 3828d19ab2d68dd6cafa6f08221e1eee572671e7 (patch) | |
tree | afd36fcf20499d876493bf24bd9f0ee926454547 /spec | |
parent | 3e764061b3209d4deee2a55851d5d564a9f19d8b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
39 files changed, 278 insertions, 351 deletions
diff --git a/spec/features/contextual_sidebar_spec.rb b/spec/features/contextual_sidebar_spec.rb index 2b671d4b3f1..132c8eb7192 100644 --- a/spec/features/contextual_sidebar_spec.rb +++ b/spec/features/contextual_sidebar_spec.rb @@ -39,74 +39,5 @@ RSpec.describe 'Contextual sidebar', :js, feature_category: :remote_development expect(page).to have_selector('.is-showing-fly-out') end end - - context 'with invite_members_in_side_nav experiment', :experiment do - it 'allows opening of modal for the candidate experience' do - stub_experiments(invite_members_in_side_nav: :candidate) - expect(experiment(:invite_members_in_side_nav)).to track(:assignment) - .with_context(group: project.group) - .on_next_instance - - visit project_path(project) - - page.within '[data-test-id="side-nav-invite-members"' do - find('[data-test-id="invite-members-button"').click - end - - expect(page).to have_content("You're inviting members to the") - end - - it 'does not have invite members link in side nav for the control experience' do - stub_experiments(invite_members_in_side_nav: :control) - expect(experiment(:invite_members_in_side_nav)).to track(:assignment) - .with_context(group: project.group) - .on_next_instance - - visit project_path(project) - - expect(page).not_to have_css('[data-test-id="side-nav-invite-members"') - end - end - end - - context 'when context is a group' do - let_it_be(:user) { create(:user) } - let_it_be(:group) do - create(:group).tap do |g| - g.add_owner(user) - end - end - - before do - sign_in(user) - end - - context 'with invite_members_in_side_nav experiment', :experiment do - it 'allows opening of modal for the candidate experience' do - stub_experiments(invite_members_in_side_nav: :candidate) - expect(experiment(:invite_members_in_side_nav)).to track(:assignment) - .with_context(group: group) - .on_next_instance - - visit group_path(group) - - page.within '[data-test-id="side-nav-invite-members"' do - find('[data-test-id="invite-members-button"').click - end - - expect(page).to have_content("You're inviting members to the") - end - - it 'does not have invite members link in side nav for the control experience' do - stub_experiments(invite_members_in_side_nav: :control) - expect(experiment(:invite_members_in_side_nav)).to track(:assignment) - .with_context(group: group) - .on_next_instance - - visit group_path(group) - - expect(page).not_to have_css('[data-test-id="side-nav-invite-members"') - end - end end end diff --git a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb index f5b02a87758..a5d6ba58ffa 100644 --- a/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb +++ b/spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb @@ -21,9 +21,8 @@ RSpec.describe 'The group dashboard', :js, feature_category: :subgroups do within_top_nav do expect(page).to have_button('Projects') expect(page).to have_button('Groups') - expect(page).to have_link('Activity') - expect(page).to have_link('Milestones') - expect(page).to have_link('Snippets') + expect(page).to have_link('Your work') + expect(page).to have_link('Explore') end end @@ -36,9 +35,8 @@ RSpec.describe 'The group dashboard', :js, feature_category: :subgroups do within_top_nav do expect(page).to have_button('Projects') expect(page).to have_button('Groups') - expect(page).not_to have_link('Activity') - expect(page).not_to have_link('Milestones') - expect(page).to have_link('Snippets') + expect(page).to have_link('Your work') + expect(page).to have_link('Explore') end end end diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb index a45e0a58ed6..1fb393e1769 100644 --- a/spec/features/dashboard/groups_list_spec.rb +++ b/spec/features/dashboard/groups_list_spec.rb @@ -230,4 +230,11 @@ RSpec.describe 'Dashboard Groups page', :js, feature_category: :subgroups do expect(page).not_to have_content(another_group.name) end end + + it 'links to the "Explore groups" page' do + sign_in(user) + visit dashboard_groups_path + + expect(page).to have_link("Explore groups", href: explore_groups_path) + end end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 099a7b14cd8..eafc41c0f40 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -20,6 +20,12 @@ RSpec.describe 'Dashboard Projects', feature_category: :projects do it_behaves_like "a dashboard page with sidebar", :dashboard_projects_path, :projects + it 'links to the "Explore projects" page' do + visit dashboard_projects_path + + expect(page).to have_link("Explore projects", href: explore_projects_path) + end + context 'when user has access to the project' do it 'shows role badge' do visit dashboard_projects_path diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb index ba40290d866..f4234b433f8 100644 --- a/spec/features/dashboard/snippets_spec.rb +++ b/spec/features/dashboard/snippets_spec.rb @@ -7,6 +7,13 @@ RSpec.describe 'Dashboard snippets', feature_category: :source_code_management d it_behaves_like 'a dashboard page with sidebar', :dashboard_snippets_path, :snippets + it 'links to the "Explore snippets" page' do + sign_in(user) + visit dashboard_snippets_path + + expect(page).to have_link("Explore snippets", href: explore_snippets_path) + end + context 'when the project has snippets' do let(:project) { create(:project, :public, creator: user) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.first_owner, project: project) } diff --git a/spec/features/explore/navbar_spec.rb b/spec/features/explore/navbar_spec.rb new file mode 100644 index 00000000000..8f281abe6a7 --- /dev/null +++ b/spec/features/explore/navbar_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe '"Explore" navbar', feature_category: :navigation do + include_context '"Explore" navbar structure' + + it_behaves_like 'verified navigation bar' do + before do + visit explore_projects_path + end + end +end diff --git a/spec/features/nav/top_nav_responsive_spec.rb b/spec/features/nav/top_nav_responsive_spec.rb index f1862a3b9b2..9ac63c26ba0 100644 --- a/spec/features/nav/top_nav_responsive_spec.rb +++ b/spec/features/nav/top_nav_responsive_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'top nav responsive', :js, feature_category: :navigation do context 'when menu is closed' do it 'has page content and hides responsive menu', :aggregate_failures do - expect(page).to have_css('.page-title', text: 'Projects') + expect(page).to have_css('.page-title', text: 'Explore projects') expect(page).to have_link('Dashboard', id: 'logo') expect(page).to have_no_css('.top-nav-responsive') @@ -34,14 +34,15 @@ RSpec.describe 'top nav responsive', :js, feature_category: :navigation do end it 'hides everything and shows responsive menu', :aggregate_failures do - expect(page).to have_no_css('.page-title', text: 'Projects') + expect(page).to have_no_css('.page-title', text: 'Explore projects') expect(page).to have_no_link('Dashboard', id: 'logo') within '.top-nav-responsive' do expect(page).to have_link(nil, href: search_path) expect(page).to have_button('Projects') expect(page).to have_button('Groups') - expect(page).to have_link('Snippets', href: dashboard_snippets_path) + expect(page).to have_link('Your work', href: dashboard_projects_path) + expect(page).to have_link('Explore', href: explore_projects_path) end end diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb index dc2fcdd7305..d6ff8c066c4 100644 --- a/spec/features/snippets/show_spec.rb +++ b/spec/features/snippets/show_spec.rb @@ -28,11 +28,10 @@ RSpec.describe 'Snippet', :js, feature_category: :source_code_management do it_behaves_like 'a dashboard page with sidebar', :dashboard_snippets_path, :snippets context 'when unauthenticated' do - it 'does not have the sidebar' do + it 'shows the "Explore" sidebar' do visit snippet_path(snippet) - expect(page).to have_title _('Snippets') - expect(page).not_to have_css('aside.nav-sidebar') + expect(page).to have_css('aside.nav-sidebar[aria-label="Explore"]') end end diff --git a/spec/finders/abuse_reports_finder_spec.rb b/spec/finders/abuse_reports_finder_spec.rb index 5b92dcd9069..d3b148375d4 100644 --- a/spec/finders/abuse_reports_finder_spec.rb +++ b/spec/finders/abuse_reports_finder_spec.rb @@ -5,8 +5,11 @@ require 'spec_helper' RSpec.describe AbuseReportsFinder, '#execute' do let_it_be(:user1) { create(:user) } let_it_be(:user2) { create(:user) } + let_it_be(:reporter) { create(:user) } let_it_be(:abuse_report_1) { create(:abuse_report, id: 20, category: 'spam', user: user1) } - let_it_be(:abuse_report_2) { create(:abuse_report, :closed, id: 30, category: 'phishing', user: user2) } + let_it_be(:abuse_report_2) do + create(:abuse_report, :closed, id: 30, category: 'phishing', user: user2, reporter: reporter) + end let(:params) { {} } @@ -26,17 +29,15 @@ RSpec.describe AbuseReportsFinder, '#execute' do end end - context 'when params[:user] is present' do - let(:params) { { user: abuse_report_1.user.username } } - - it 'returns abuse reports for the specified user' do - expect(subject).to match_array([abuse_report_1]) + shared_examples 'returns filtered reports' do |filter_field| + it "returns abuse reports filtered by #{filter_field}_id" do + expect(subject).to match_array(filtered_reports) end - context 'when no user has username = params[:user]' do + context "when no user has username = params[:#{filter_field}]" do before do allow(User).to receive_message_chain(:by_username, :pick) - .with(params[:user]) + .with(params[filter_field]) .with(:id) .and_return(nil) end @@ -47,6 +48,20 @@ RSpec.describe AbuseReportsFinder, '#execute' do end end + context 'when params[:user] is present' do + it_behaves_like 'returns filtered reports', :user do + let(:params) { { user: user1.username } } + let(:filtered_reports) { [abuse_report_1] } + end + end + + context 'when params[:reporter] is present' do + it_behaves_like 'returns filtered reports', :reporter do + let(:params) { { reporter: reporter.username } } + let(:filtered_reports) { [abuse_report_2] } + end + end + context 'when params[:status] is present' do context 'when value is "open"' do let(:params) { { status: 'open' } } diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js index 7f881a92964..62db59f8f57 100644 --- a/spec/frontend/boards/components/board_form_spec.js +++ b/spec/frontend/boards/components/board_form_spec.js @@ -136,7 +136,7 @@ describe('BoardForm', () => { it('passes correct primary action text and variant', () => { expect(findModalActionPrimary().text).toBe('Create board'); - expect(findModalActionPrimary().attributes[0].variant).toBe('confirm'); + expect(findModalActionPrimary().attributes.variant).toBe('confirm'); }); it('does not render delete confirmation message', () => { @@ -248,7 +248,7 @@ describe('BoardForm', () => { it('passes correct primary action text and variant', () => { expect(findModalActionPrimary().text).toBe('Save changes'); - expect(findModalActionPrimary().attributes[0].variant).toBe('confirm'); + expect(findModalActionPrimary().attributes.variant).toBe('confirm'); }); it('does not render delete confirmation message', () => { @@ -369,7 +369,7 @@ describe('BoardForm', () => { it('passes correct primary action text and variant', () => { createComponent({ canAdminBoard: true, currentPage: formType.delete }); expect(findModalActionPrimary().text).toBe('Delete'); - expect(findModalActionPrimary().attributes[0].variant).toBe('danger'); + expect(findModalActionPrimary().attributes.variant).toBe('danger'); }); it('renders delete confirmation message', () => { diff --git a/spec/frontend/ci/pipeline_schedules/components/take_ownership_modal_legacy_spec.js b/spec/frontend/ci/pipeline_schedules/components/take_ownership_modal_legacy_spec.js index 7e6d4ec4bf8..e4ff9a0545b 100644 --- a/spec/frontend/ci/pipeline_schedules/components/take_ownership_modal_legacy_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/take_ownership_modal_legacy_spec.js @@ -25,14 +25,12 @@ describe('Take ownership modal', () => { const actionPrimary = findModal().props('actionPrimary'); expect(actionPrimary.attributes).toEqual( - expect.objectContaining([ - { - category: 'primary', - variant: 'confirm', - href: url, - 'data-method': 'post', - }, - ]), + expect.objectContaining({ + category: 'primary', + variant: 'confirm', + href: url, + 'data-method': 'post', + }), ); }); diff --git a/spec/frontend/clusters/agents/components/revoke_token_button_spec.js b/spec/frontend/clusters/agents/components/revoke_token_button_spec.js index 9199c76585f..ed7c940bb04 100644 --- a/spec/frontend/clusters/agents/components/revoke_token_button_spec.js +++ b/spec/frontend/clusters/agents/components/revoke_token_button_spec.js @@ -45,7 +45,7 @@ describe('RevokeTokenButton', () => { const findInput = () => wrapper.findComponent(GlFormInput); const findTooltip = () => wrapper.findComponent(GlTooltip); const findPrimaryAction = () => findModal().props('actionPrimary'); - const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[0][attr]; + const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[attr]; const createMockApolloProvider = ({ mutationResponse }) => { revokeSpy = jest.fn().mockResolvedValue(mutationResponse); diff --git a/spec/frontend/clusters_list/components/delete_agent_button_spec.js b/spec/frontend/clusters_list/components/delete_agent_button_spec.js index a0642996a20..53cf67bca0f 100644 --- a/spec/frontend/clusters_list/components/delete_agent_button_spec.js +++ b/spec/frontend/clusters_list/components/delete_agent_button_spec.js @@ -33,7 +33,7 @@ describe('DeleteAgentButton', () => { const findDeleteBtn = () => wrapper.findComponent(GlButton); const findInput = () => wrapper.findComponent(GlFormInput); const findPrimaryAction = () => findModal().props('actionPrimary'); - const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[0][attr]; + const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[attr]; const findDeleteAgentButtonTooltip = () => wrapper.findByTestId('delete-agent-button-tooltip'); const getTooltipText = (el) => { const binding = getBinding(el, 'gl-tooltip'); diff --git a/spec/frontend/environments/canary_update_modal_spec.js b/spec/frontend/environments/canary_update_modal_spec.js index 7eb2a54a080..a101ed4e00a 100644 --- a/spec/frontend/environments/canary_update_modal_spec.js +++ b/spec/frontend/environments/canary_update_modal_spec.js @@ -45,7 +45,7 @@ describe('/environments/components/canary_update_modal.vue', () => { modalId: 'confirm-canary-change', actionPrimary: { text: 'Change ratio', - attributes: [{ variant: 'confirm' }], + attributes: { variant: 'confirm' }, }, actionCancel: { text: 'Cancel' }, }); diff --git a/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js b/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js index 4851e03f713..b06e0340991 100644 --- a/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js +++ b/spec/frontend/feature_flags/components/configure_feature_flags_modal_spec.js @@ -50,7 +50,7 @@ describe('Configure Feature Flags Modal', () => { }); it('should default disable the primary action', () => { - const [{ disabled }] = findSecondaryAction().attributes; + const { disabled } = findSecondaryAction().attributes; expect(disabled).toBe(true); }); @@ -116,7 +116,7 @@ describe('Configure Feature Flags Modal', () => { it('should enable the secondary action', async () => { findProjectNameInput().vm.$emit('input', provide.projectName); await nextTick(); - const [{ disabled }] = findSecondaryAction().attributes; + const { disabled } = findSecondaryAction().attributes; expect(disabled).toBe(false); }); }); diff --git a/spec/frontend/ide/components/new_dropdown/modal_spec.js b/spec/frontend/ide/components/new_dropdown/modal_spec.js index a0c454dde82..36c3d323e63 100644 --- a/spec/frontend/ide/components/new_dropdown/modal_spec.js +++ b/spec/frontend/ide/components/new_dropdown/modal_spec.js @@ -93,11 +93,11 @@ describe('new file modal component', () => { it('renders modal', () => { expect(findGlModal().props()).toMatchObject({ actionCancel: { - attributes: [{ variant: 'default' }], + attributes: { variant: 'default' }, text: 'Cancel', }, actionPrimary: { - attributes: [{ variant: 'confirm' }], + attributes: { variant: 'confirm' }, text: 'Create file', }, actionSecondary: null, @@ -169,7 +169,7 @@ describe('new file modal component', () => { expect(findGlModal().props()).toMatchObject({ title: modalTitle, actionPrimary: { - attributes: [{ variant: 'confirm' }], + attributes: { variant: 'confirm' }, text: btnTitle, }, }); @@ -297,7 +297,7 @@ describe('new file modal component', () => { expect(findGlModal().props()).toMatchObject({ title, actionPrimary: { - attributes: [{ variant: 'confirm' }], + attributes: { variant: 'confirm' }, text: title, }, }); diff --git a/spec/frontend/invite_members/components/invite_members_trigger_spec.js b/spec/frontend/invite_members/components/invite_members_trigger_spec.js index 699a0ad5809..cdb6182e2ae 100644 --- a/spec/frontend/invite_members/components/invite_members_trigger_spec.js +++ b/spec/frontend/invite_members/components/invite_members_trigger_spec.js @@ -1,10 +1,9 @@ -import { GlButton, GlLink, GlIcon, GlDropdownItem } from '@gitlab/ui'; +import { GlButton, GlLink, GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import eventHub from '~/invite_members/event_hub'; import { TRIGGER_ELEMENT_BUTTON, - TRIGGER_ELEMENT_SIDE_NAV, TRIGGER_DEFAULT_QA_SELECTOR, TRIGGER_ELEMENT_WITH_EMOJI, TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI, @@ -22,7 +21,6 @@ let findButton; const triggerComponent = { button: GlButton, anchor: GlLink, - 'side-nav': GlLink, 'text-emoji': GlLink, 'dropdown-text-emoji': GlDropdownItem, }; @@ -48,10 +46,6 @@ const triggerItems = [ triggerElement: 'anchor', }, { - triggerElement: TRIGGER_ELEMENT_SIDE_NAV, - icon: 'plus', - }, - { triggerElement: TRIGGER_ELEMENT_WITH_EMOJI, icon: 'shaking_hands', }, @@ -101,17 +95,6 @@ describe.each(triggerItems)('with triggerElement as %s', (triggerItem) => { }); }); -describe('side-nav with icon', () => { - it('includes the specified icon with correct size when triggerElement is link', () => { - const findIcon = () => wrapper.findComponent(GlIcon); - - createComponent({ triggerElement: TRIGGER_ELEMENT_SIDE_NAV, icon: 'plus' }); - - expect(findIcon().exists()).toBe(true); - expect(findIcon().props('name')).toBe('plus'); - }); -}); - describe('link with emoji', () => { it('includes the specified icon with correct size when triggerElement is link', () => { const findEmoji = () => wrapper.findComponent(GlEmoji); diff --git a/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js b/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js index d60043f33f7..712269a1e83 100644 --- a/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js +++ b/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js @@ -64,13 +64,11 @@ describe('Job Retry Forward Deployment Modal', () => { beforeEach(createWrapper); it('should correctly configure the primary action', () => { - expect(findModal().props('actionPrimary').attributes).toMatchObject([ - { - 'data-method': 'post', - href: job.retry_path, - variant: 'danger', - }, - ]); + expect(findModal().props('actionPrimary').attributes).toMatchObject({ + 'data-method': 'post', + href: job.retry_path, + variant: 'danger', + }); }); }); }); diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_modal_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_modal_spec.js index 992e2eb6f3e..860f9b3a0c1 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_modal_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_modal_spec.js @@ -30,7 +30,7 @@ describe('Delete Modal', () => { const expectPrimaryActionStatus = (disabled = true) => expect(findModal().props('actionPrimary')).toMatchObject( expect.objectContaining({ - attributes: [{ variant: 'danger' }, { disabled }], + attributes: { variant: 'danger', disabled }, }), ); diff --git a/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js b/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js index 9c1ebf5a2eb..d0817a8678e 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js @@ -45,7 +45,7 @@ describe('DeleteModal', () => { it('passes actionPrimary prop', () => { expect(findModal().props('actionPrimary')).toStrictEqual({ text: 'Permanently delete', - attributes: [{ variant: 'danger' }, { category: 'primary' }], + attributes: { variant: 'danger', category: 'primary' }, }); }); diff --git a/spec/frontend/pages/profiles/password_prompt/password_prompt_modal_spec.js b/spec/frontend/pages/profiles/password_prompt/password_prompt_modal_spec.js index 439bb342d01..18a0098a715 100644 --- a/spec/frontend/pages/profiles/password_prompt/password_prompt_modal_spec.js +++ b/spec/frontend/pages/profiles/password_prompt/password_prompt_modal_spec.js @@ -25,8 +25,7 @@ describe('Password prompt modal', () => { const findField = () => wrapper.findByTestId('password-prompt-field'); const findModal = () => wrapper.findComponent(GlModal); const findConfirmBtn = () => findModal().props('actionPrimary'); - const findConfirmBtnDisabledState = () => - findModal().props('actionPrimary').attributes[2].disabled; + const findConfirmBtnDisabledState = () => findModal().props('actionPrimary').attributes.disabled; const findCancelBtn = () => findModal().props('actionCancel'); diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js index 5846174296a..9ca45bfb655 100644 --- a/spec/frontend/repository/components/delete_blob_modal_spec.js +++ b/spec/frontend/repository/components/delete_blob_modal_spec.js @@ -183,7 +183,7 @@ describe('DeleteBlobModal', () => { }); it('disables submit button', async () => { - expect(findModal().props('actionPrimary').attributes[0]).toEqual( + expect(findModal().props('actionPrimary').attributes).toEqual( expect.objectContaining({ disabled: true }), ); }); @@ -203,7 +203,7 @@ describe('DeleteBlobModal', () => { }); it('enables submit button', async () => { - expect(findModal().props('actionPrimary').attributes[0]).toEqual( + expect(findModal().props('actionPrimary').attributes).toEqual( expect.objectContaining({ disabled: false }), ); }); diff --git a/spec/frontend/repository/components/new_directory_modal_spec.js b/spec/frontend/repository/components/new_directory_modal_spec.js index d70021650ed..c920159375f 100644 --- a/spec/frontend/repository/components/new_directory_modal_spec.js +++ b/spec/frontend/repository/components/new_directory_modal_spec.js @@ -181,7 +181,7 @@ describe('NewDirectoryModal', () => { it('disables submit button', async () => { await fillForm({ dirName: '', branchName: '', commitMessage: '' }); - expect(findModal().props('actionPrimary').attributes[0].disabled).toBe(true); + expect(findModal().props('actionPrimary').attributes.disabled).toBe(true); }); it('creates an alert error', async () => { diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js index 76a50c41a66..0f4edb19632 100644 --- a/spec/frontend/repository/components/tree_content_spec.js +++ b/spec/frontend/repository/components/tree_content_spec.js @@ -1,6 +1,5 @@ import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; -import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.graphql'; import FilePreview from '~/repository/components/preview/index.vue'; import FileTable from '~/repository/components/table/index.vue'; import TreeContent from 'jh_else_ce/repository/components/tree_content.vue'; @@ -33,12 +32,6 @@ function factory(path, appoloMockResponse = mockResponse) { mocks: { $apollo, }, - provide: { - glFeatures: { - increasePageSizeExponentially: true, - paginatedTreeGraphqlQuery: true, - }, - }, }); } @@ -167,37 +160,6 @@ describe('Repository table component', () => { expect(findFileTable().props('hasMore')).toBe(limitReached); }); - - it.each` - fetchCounter | pageSize - ${0} | ${10} - ${2} | ${30} - ${4} | ${50} - ${6} | ${70} - ${8} | ${90} - ${10} | ${100} - ${20} | ${100} - ${100} | ${100} - ${200} | ${100} - `('exponentially increases page size, to a maximum of 100', ({ fetchCounter, pageSize }) => { - factory('/'); - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - vm.setData({ fetchCounter }); - - vm.vm.fetchFiles(); - - expect($apollo.query).toHaveBeenCalledWith({ - query: paginatedTreeQuery, - variables: { - pageSize, - nextPageCursor: '', - path: '/', - projectPath: '', - ref: '', - }, - }); - }); }); describe('commit data', () => { diff --git a/spec/frontend/repository/components/upload_blob_modal_spec.js b/spec/frontend/repository/components/upload_blob_modal_spec.js index 21699f81900..f7c31f91cd7 100644 --- a/spec/frontend/repository/components/upload_blob_modal_spec.js +++ b/spec/frontend/repository/components/upload_blob_modal_spec.js @@ -53,9 +53,9 @@ describe('UploadBlobModal', () => { const findBranchName = () => wrapper.findComponent(GlFormInput); const findMrToggle = () => wrapper.findComponent(GlToggle); const findUploadDropzone = () => wrapper.findComponent(UploadDropzone); - const actionButtonDisabledState = () => findModal().props('actionPrimary').attributes[0].disabled; - const cancelButtonDisabledState = () => findModal().props('actionCancel').attributes[0].disabled; - const actionButtonLoadingState = () => findModal().props('actionPrimary').attributes[0].loading; + const actionButtonDisabledState = () => findModal().props('actionPrimary').attributes.disabled; + const cancelButtonDisabledState = () => findModal().props('actionCancel').attributes.disabled; + const actionButtonLoadingState = () => findModal().props('actionPrimary').attributes.loading; describe.each` canPushCode | displayBranchName | displayForkedBranchMessage diff --git a/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js b/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js index 715f66d305a..a7c3867c359 100644 --- a/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js +++ b/spec/frontend/sidebar/components/time_tracking/create_timelog_form_spec.js @@ -47,8 +47,8 @@ describe('Create Timelog Form', () => { const findAlert = () => wrapper.findComponent(GlAlert); const findDocsLink = () => wrapper.findByTestId('timetracking-docs-link'); const findSaveButton = () => findModal().props('actionPrimary'); - const findSaveButtonLoadingState = () => findSaveButton().attributes[0].loading; - const findSaveButtonDisabledState = () => findSaveButton().attributes[0].disabled; + const findSaveButtonLoadingState = () => findSaveButton().attributes.loading; + const findSaveButtonDisabledState = () => findSaveButton().attributes.disabled; const submitForm = () => findForm().trigger('submit'); diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js index a5faad967fc..ae15dd55644 100644 --- a/spec/frontend/super_sidebar/components/user_bar_spec.js +++ b/spec/frontend/super_sidebar/components/user_bar_spec.js @@ -13,6 +13,7 @@ describe('UserBar component', () => { const findCreateMenu = () => wrapper.findComponent(CreateMenu); const findCounter = (at) => wrapper.findAllComponents(Counter).at(at); const findMergeRequestMenu = () => wrapper.findComponent(MergeRequestMenu); + const findBrandLogo = () => wrapper.findByTestId('brand-header-custom-logo'); const createWrapper = (extraSidebarData = {}) => { wrapper = shallowMountExtended(UserBar, { @@ -55,6 +56,11 @@ describe('UserBar component', () => { expect(findCounter(2).props('href')).toBe('/dashboard/todos'); expect(findCounter(2).props('label')).toBe(__('To-Do list')); }); + + it('renders branding logo', () => { + expect(findBrandLogo().exists()).toBe(true); + expect(findBrandLogo().attributes('src')).toBe(sidebarData.logo_url); + }); }); describe('GitLab Next badge', () => { diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js index 2cd055e6d07..32f56e5c107 100644 --- a/spec/frontend/super_sidebar/mock_data.js +++ b/spec/frontend/super_sidebar/mock_data.js @@ -68,6 +68,7 @@ export const sidebarData = { name: 'Administrator', username: 'root', avatar_url: 'path/to/img_administrator', + logo_url: 'path/to/logo', assigned_open_issues_count: 1, todos_pending_count: 3, issues_dashboard_path: 'path/to/issues', diff --git a/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_modal_spec.js b/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_modal_spec.js index b03b6bfb61d..d7f94c00d09 100644 --- a/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_modal_spec.js +++ b/spec/frontend/vue_shared/components/confirm_danger/confirm_danger_modal_spec.js @@ -24,7 +24,7 @@ describe('Confirm Danger Modal', () => { const findAdditionalMessage = () => wrapper.findByTestId('confirm-danger-message'); const findPrimaryAction = () => findModal().props('actionPrimary'); const findCancelAction = () => findModal().props('actionCancel'); - const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[0][attr]; + const findPrimaryActionAttributes = (attr) => findPrimaryAction().attributes[attr]; const createComponent = ({ provide = {} } = {}) => shallowMountExtended(ConfirmDangerModal, { diff --git a/spec/helpers/explore_helper_spec.rb b/spec/helpers/explore_helper_spec.rb index 4ae1b738858..68c5289a85f 100644 --- a/spec/helpers/explore_helper_spec.rb +++ b/spec/helpers/explore_helper_spec.rb @@ -12,7 +12,7 @@ RSpec.describe ExploreHelper do describe '#explore_nav_links' do it 'has all the expected links by default' do - menu_items = [:projects, :groups, :snippets] + menu_items = [:projects, :groups, :topics, :snippets] expect(helper.explore_nav_links).to contain_exactly(*menu_items) end diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb index ce5ac2e5404..2b69557b840 100644 --- a/spec/helpers/nav/top_nav_helper_spec.rb +++ b/spec/helpers/nav/top_nav_helper_spec.rb @@ -56,6 +56,7 @@ RSpec.describe Nav::TopNavHelper do expected_primary = [ { href: '/explore', icon: 'project', id: 'project', title: 'Projects' }, { href: '/explore/groups', icon: 'group', id: 'groups', title: 'Groups' }, + { href: '/explore/projects/topics', icon: 'labels', id: 'topics', title: 'Topics' }, { href: '/explore/snippets', icon: 'snippet', id: 'snippets', title: 'Snippets' } ].map do |item| ::Gitlab::Nav::TopNavMenuItem.build(**item) @@ -79,6 +80,12 @@ RSpec.describe Nav::TopNavHelper do css_class: 'dashboard-shortcuts-groups' }, { + href: '/explore/projects/topics', + id: 'topics-shortcut', + title: 'Topics', + css_class: 'dashboard-shortcuts-topics' + }, + { href: '/explore/snippets', id: 'snippets-shortcut', title: 'Snippets', @@ -320,20 +327,6 @@ RSpec.describe Nav::TopNavHelper do context 'with milestones' do let(:with_milestones) { true } - it 'has expected :primary' do - expected_header = ::Gitlab::Nav::TopNavMenuHeader.build( - title: 'Explore' - ) - expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( - data: { **menu_data_tracking_attrs('milestones') }, - href: '/dashboard/milestones', - icon: 'clock', - id: 'milestones', - title: 'Milestones' - ) - expect(subject[:primary]).to eq([expected_header, expected_primary]) - end - it 'has expected :shortcuts' do expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build( id: 'milestones-shortcut', @@ -348,23 +341,6 @@ RSpec.describe Nav::TopNavHelper do context 'with snippets' do let(:with_snippets) { true } - it 'has expected :primary' do - expected_header = ::Gitlab::Nav::TopNavMenuHeader.build( - title: 'Explore' - ) - expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( - data: { - qa_selector: 'snippets_link', - **menu_data_tracking_attrs('snippets') - }, - href: '/dashboard/snippets', - icon: 'snippet', - id: 'snippets', - title: 'Snippets' - ) - expect(subject[:primary]).to eq([expected_header, expected_primary]) - end - it 'has expected :shortcuts' do expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build( id: 'snippets-shortcut', @@ -379,20 +355,6 @@ RSpec.describe Nav::TopNavHelper do context 'with activity' do let(:with_activity) { true } - it 'has expected :primary' do - expected_header = ::Gitlab::Nav::TopNavMenuHeader.build( - title: 'Explore' - ) - expected_primary = ::Gitlab::Nav::TopNavMenuItem.build( - data: { **menu_data_tracking_attrs('activity') }, - href: '/dashboard/activity', - icon: 'history', - id: 'activity', - title: 'Activity' - ) - expect(subject[:primary]).to eq([expected_header, expected_primary]) - end - it 'has expected :shortcuts' do expected_shortcuts = ::Gitlab::Nav::TopNavMenuItem.build( id: 'activity-shortcut', diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index 671f1ee1a0a..f457ba06074 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -275,7 +275,8 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do it 'sends a create_repository message without arguments' do expect_any_instance_of(Gitaly::RepositoryService::Stub) .to receive(:create_repository) - .with(gitaly_request_with_path(storage_name, relative_path).and(gitaly_request_with_params(default_branch: '')), kind_of(Hash)) + .with(gitaly_request_with_path(storage_name, relative_path) + .and(gitaly_request_with_params(default_branch: '')), kind_of(Hash)) .and_return(double) client.create_repository @@ -284,11 +285,23 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do it 'sends a create_repository message with default branch' do expect_any_instance_of(Gitaly::RepositoryService::Stub) .to receive(:create_repository) - .with(gitaly_request_with_path(storage_name, relative_path).and(gitaly_request_with_params(default_branch: 'default-branch-name')), kind_of(Hash)) + .with(gitaly_request_with_path(storage_name, relative_path) + .and(gitaly_request_with_params(default_branch: 'default-branch-name')), kind_of(Hash)) .and_return(double) client.create_repository('default-branch-name') end + + it 'sends a create_repository message with default branch containing non ascii chars' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:create_repository) + .with(gitaly_request_with_path(storage_name, relative_path) + .and(gitaly_request_with_params( + default_branch: Gitlab::EncodingHelper.encode_binary('feature/新機能'))), kind_of(Hash) + ).and_return(double) + + client.create_repository('feature/新機能') + end end describe '#create_from_snapshot' do diff --git a/spec/lib/sidebars/groups/menus/invite_team_members_menu_spec.rb b/spec/lib/sidebars/groups/menus/invite_team_members_menu_spec.rb deleted file mode 100644 index a79e5182f45..00000000000 --- a/spec/lib/sidebars/groups/menus/invite_team_members_menu_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Sidebars::Groups::Menus::InviteTeamMembersMenu do - let_it_be(:owner) { create(:user) } - let_it_be(:guest) { create(:user) } - let_it_be(:group) do - build(:group).tap do |g| - g.add_owner(owner) - end - end - - let(:context) { Sidebars::Groups::Context.new(current_user: owner, container: group) } - - subject(:invite_menu) { described_class.new(context) } - - context 'when the group is viewed by an owner of the group' do - describe '#render?' do - it 'renders the Invite team members link' do - expect(invite_menu.render?).to eq(true) - end - - context 'when the group already has at least 2 members' do - before do - group.add_guest(guest) - end - - it 'does not render the link' do - expect(invite_menu.render?).to eq(false) - end - end - end - - describe '#title' do - it 'displays the correct Invite team members text for the link in the side nav' do - expect(invite_menu.title).to eq('Invite members') - end - end - end - - context 'when the group is viewed by a guest user without admin permissions' do - let(:context) { Sidebars::Groups::Context.new(current_user: guest, container: group) } - - before do - group.add_guest(guest) - end - - describe '#render?' do - it 'does not render the link' do - expect(subject.render?).to eq(false) - end - end - end -end diff --git a/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb b/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb deleted file mode 100644 index 9838aa8c3e3..00000000000 --- a/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Sidebars::Projects::Menus::InviteTeamMembersMenu do - let_it_be(:project) { create(:project) } - let_it_be(:guest) { create(:user) } - - let(:context) { Sidebars::Projects::Context.new(current_user: owner, container: project) } - - subject(:invite_menu) { described_class.new(context) } - - context 'when the project is viewed by an owner of the group' do - let(:owner) { project.first_owner } - - describe '#render?' do - it 'renders the Invite team members link' do - expect(invite_menu.render?).to eq(true) - end - - context 'when the project already has at least 2 members' do - before do - project.add_guest(guest) - end - - it 'does not render the link' do - expect(invite_menu.render?).to eq(false) - end - end - end - - describe '#title' do - it 'displays the correct Invite team members text for the link in the side nav' do - expect(invite_menu.title).to eq('Invite members') - end - end - end - - context 'when the project is viewed by a guest user without admin permissions' do - let(:context) { Sidebars::Projects::Context.new(current_user: guest, container: project) } - - before do - project.add_guest(guest) - end - - describe '#render?' do - it 'does not render' do - expect(invite_menu.render?).to eq(false) - end - end - end -end diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 30639f5a580..9026a870138 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -71,9 +71,18 @@ RSpec.describe AbuseReport, feature_category: :insider_threat do end describe 'scopes' do - let_it_be(:report1) { create(:abuse_report) } + let_it_be(:reporter) { create(:user) } + let_it_be(:report1) { create(:abuse_report, reporter: reporter) } let_it_be(:report2) { create(:abuse_report, :closed, category: 'phishing') } + describe '.by_reporter_id' do + subject(:results) { described_class.by_reporter_id(reporter.id) } + + it 'returns reports with reporter_id equal to the given user id' do + expect(subject).to match_array([report1]) + end + end + describe '.open' do subject(:results) { described_class.open } diff --git a/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_spec.rb index 16f78b67b5c..7519389ab49 100644 --- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do 'title' => 'new title', 'description' => 'new description', 'confidential' => true, - 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s + 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_gid.to_s } end @@ -43,14 +43,14 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do expect(created_work_item.work_item_type.base_type).to eq('task') expect(mutation_response['workItem']).to include( input.except('workItemTypeId').merge( - 'id' => created_work_item.to_global_id.to_s, + 'id' => created_work_item.to_gid.to_s, 'workItemType' => hash_including('name' => 'Task') ) ) end context 'when input is invalid' do - let(:input) { { 'title' => '', 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s } } + let(:input) { { 'title' => '', 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_gid.to_s } } it 'does not create and returns validation errors' do expect do @@ -98,8 +98,8 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do let(:input) do { title: 'item1', - workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s, - hierarchyWidget: { 'parentId' => parent.to_global_id.to_s } + workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s, + hierarchyWidget: { 'parentId' => parent.to_gid.to_s } } end @@ -110,7 +110,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do expect(widgets_response).to include( { 'children' => { 'edges' => [] }, - 'parent' => { 'id' => parent.to_global_id.to_s }, + 'parent' => { 'id' => parent.to_gid.to_s }, 'type' => 'HIERARCHY' } ) @@ -137,6 +137,40 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do expect(graphql_errors.first['message']).to include('No object found for `parentId') end end + + context 'when adjacent is already in place' do + let_it_be(:adjacent) { create(:work_item, :task, project: project) } + + let(:work_item) { WorkItem.last } + + let(:input) do + { + title: 'item1', + workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s, + hierarchyWidget: { 'parentId' => parent.to_gid.to_s } + } + end + + before(:all) do + create(:parent_link, work_item_parent: parent, work_item: adjacent, relative_position: 0) + end + + it 'creates work item and sets the relative position to be AFTER adjacent' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change(WorkItem, :count).by(1) + + expect(response).to have_gitlab_http_status(:success) + expect(widgets_response).to include( + { + 'children' => { 'edges' => [] }, + 'parent' => { 'id' => parent.to_gid.to_s }, + 'type' => 'HIERARCHY' + } + ) + expect(work_item.parent_link.relative_position).to be > adjacent.parent_link.relative_position + end + end end context 'when unsupported widget input is sent' do @@ -144,7 +178,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do { 'title' => 'new title', 'description' => 'new description', - 'workItemTypeId' => WorkItems::Type.default_by_type(:test_case).to_global_id.to_s, + 'workItemTypeId' => WorkItems::Type.default_by_type(:test_case).to_gid.to_s, 'hierarchyWidget' => {} } end @@ -181,8 +215,8 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do let(:input) do { title: 'some WI', - workItemTypeId: WorkItems::Type.default_by_type(:task).to_global_id.to_s, - milestoneWidget: { 'milestoneId' => milestone.to_global_id.to_s } + workItemTypeId: WorkItems::Type.default_by_type(:task).to_gid.to_s, + milestoneWidget: { 'milestoneId' => milestone.to_gid.to_s } } end @@ -196,7 +230,7 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do expect(widgets_response).to include( { 'type' => 'MILESTONE', - 'milestone' => { 'id' => milestone.to_global_id.to_s } + 'milestone' => { 'id' => milestone.to_gid.to_s } } ) end diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb index 5884847eac3..a989ecf9c07 100644 --- a/spec/services/work_items/parent_links/create_service_spec.rb +++ b/spec/services/work_items/parent_links/create_service_spec.rb @@ -68,6 +68,40 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol end end + context 'when adjacent is already in place' do + using RSpec::Parameterized::TableSyntax + + let_it_be_with_reload(:parent_item) { create(:work_item, :objective, project: project) } + let_it_be_with_reload(:current_item) { create(:work_item, :objective, project: project) } + + let_it_be_with_reload(:adjacent) do + create(:work_item, :objective, project: project) + end + + let_it_be_with_reload(:link_to_adjacent) do + create(:parent_link, work_item_parent: parent_item, work_item: adjacent) + end + + subject { described_class.new(parent_item, user, { target_issuable: current_item }).execute } + + where(:adjacent_position, :expected_order) do + -100 | lazy { [adjacent, current_item] } + 0 | lazy { [adjacent, current_item] } + 100 | lazy { [adjacent, current_item] } + end + + with_them do + before do + link_to_adjacent.update!(relative_position: adjacent_position) + end + + it 'sets relative positions' do + expect { subject }.to change(parent_link_class, :count).by(1) + expect(parent_item.work_item_children_by_relative_position).to eq(expected_order) + end + end + end + context 'when there are tasks to relate' do let(:params) { { issuable_references: [task1, task2] } } diff --git a/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb new file mode 100644 index 00000000000..8d834c9a4f8 --- /dev/null +++ b/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::Widgets::HierarchyService::CreateService, feature_category: :portfolio_management do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:parent_item) { create(:work_item, project: project) } + + let(:widget) { parent_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } } + + shared_examples 'raises a WidgetError' do + it { expect { subject }.to raise_error(described_class::WidgetError, message) } + end + + before(:all) do + project.add_developer(user) + end + + describe '#create' do + subject { described_class.new(widget: widget, current_user: user).after_create_in_transaction(params: params) } + + context 'when invalid params are present' do + let(:params) { { other_parent: 'parent_work_item' } } + + it_behaves_like 'raises a WidgetError' do + let(:message) { 'One or more arguments are invalid: other_parent.' } + end + end + end +end diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index 8b03d8a2770..866a97b0e3c 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -259,3 +259,30 @@ RSpec.shared_context 'dashboard navbar structure' do ] end end + +RSpec.shared_context '"Explore" navbar structure' do + let(:structure) do + [ + { + nav_item: "Explore", + nav_sub_items: [] + }, + { + nav_item: _("Projects"), + nav_sub_items: [] + }, + { + nav_item: _("Groups"), + nav_sub_items: [] + }, + { + nav_item: _("Topics"), + nav_sub_items: [] + }, + { + nav_item: _("Snippets"), + nav_sub_items: [] + } + ] + end +end |