From 1d21e1712158ee4e3cf8b71b45ead662529fc3f8 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 31 Oct 2023 18:07:22 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- spec/features/issues/issue_state_spec.rb | 4 +- spec/features/issues/move_spec.rb | 4 +- .../features/projects/work_items/work_item_spec.rb | 2 + .../components/design_notes/design_note_spec.js | 43 ++- .../issues/show/components/header_actions_spec.js | 52 ++- .../components/move/issuable_move_dropdown_spec.js | 358 +++++---------------- .../components/work_item_milestone_spec.js | 53 ++- .../work_items/components/work_item_parent_spec.js | 5 - spec/helpers/environments_helper_spec.rb | 4 +- spec/helpers/ide_helper_spec.rb | 2 +- spec/helpers/operations_helper_spec.rb | 2 +- .../background_migration/batched_migration_spec.rb | 11 +- .../batched_migration_wrapper_spec.rb | 22 +- .../prometheus_metrics_spec.rb | 13 +- spec/lib/gitlab/database/bulk_update_spec.rb | 2 +- .../lib/gitlab/database/loose_foreign_keys_spec.rb | 16 +- .../migrations/constraints_helpers_spec.rb | 40 +-- .../database/migrations/instrumentation_spec.rb | 16 +- .../test_batched_background_runner_spec.rb | 104 ++++-- .../detached_partition_dropper_spec.rb | 64 ++-- .../database/partitioning/monthly_strategy_spec.rb | 36 ++- .../partitioning/sliding_list_strategy_spec.rb | 27 +- .../index_helpers_spec.rb | 7 +- .../table_management_helpers_spec.rb | 60 ++-- .../gitlab/database/postgres_constraint_spec.rb | 16 +- spec/lib/gitlab/database/tables_truncate_spec.rb | 2 +- ...esign_id_note_id_index_for_self_managed_spec.rb | 114 +++++++ spec/presenters/clusters/cluster_presenter_spec.rb | 2 +- spec/rubocop/cop/gitlab/doc_url_spec.rb | 6 +- spec/serializers/issue_entity_spec.rb | 2 +- .../features/work_items_shared_examples.rb | 4 +- 31 files changed, 578 insertions(+), 515 deletions(-) create mode 100644 spec/migrations/20231016001000_fix_design_user_mentions_design_id_note_id_index_for_self_managed_spec.rb (limited to 'spec') diff --git a/spec/features/issues/issue_state_spec.rb b/spec/features/issues/issue_state_spec.rb index 3fe49ff7080..125329764c6 100644 --- a/spec/features/issues/issue_state_spec.rb +++ b/spec/features/issues/issue_state_spec.rb @@ -51,7 +51,7 @@ RSpec.describe 'issue state', :js, feature_category: :team_planning do find('#new-actions-header-dropdown > button').click end - it_behaves_like 'issue closed', '.dropdown-menu-right' + it_behaves_like 'issue closed', '.gl-new-dropdown-contents' end context 'when clicking the bottom `Close issue` button', :aggregate_failures do @@ -74,7 +74,7 @@ RSpec.describe 'issue state', :js, feature_category: :team_planning do find('#new-actions-header-dropdown > button').click end - it_behaves_like 'issue reopened', '.dropdown-menu-right' + it_behaves_like 'issue reopened', '.gl-new-dropdown-contents' end context 'when clicking the bottom `Reopen issue` button', :aggregate_failures do diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 4a38373db71..eca52a3a2c3 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -44,7 +44,7 @@ RSpec.describe 'issue move to another project', feature_category: :team_planning it 'moving issue to another project', :js do click_button _('Move issue') wait_for_requests - all('.gl-dropdown-item')[0].click + all('.gl-new-dropdown-item')[0].click click_button _('Move') expect(page).to have_content("Text with #{cross_reference}#{mr.to_reference}") @@ -116,7 +116,7 @@ RSpec.describe 'issue move to another project', feature_category: :team_planning click_button _('Move issue') wait_for_requests - find('.gl-dropdown-item', text: project_title).click + find('.gl-new-dropdown-item', text: project_title).click click_button _('Move') end diff --git a/spec/features/projects/work_items/work_item_spec.rb b/spec/features/projects/work_items/work_item_spec.rb index 09a8636359d..876444e451a 100644 --- a/spec/features/projects/work_items/work_item_spec.rb +++ b/spec/features/projects/work_items/work_item_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Work item', :js, feature_category: :team_planning do + include ListboxHelpers + let_it_be_with_reload(:user) { create(:user, :no_super_sidebar) } let_it_be_with_reload(:user2) { create(:user, :no_super_sidebar, name: 'John') } diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js index 91c6acd8890..28b264cede9 100644 --- a/spec/frontend/design_management/components/design_notes/design_note_spec.js +++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js @@ -51,13 +51,19 @@ describe('Design note component', () => { const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown); const findDropdownItems = () => findDropdown().findAllComponents(GlDisclosureDropdownItem); const findEditDropdownItem = () => findDropdownItems().at(0); - const findDeleteDropdownItem = () => findDropdownItems().at(1); + const findCopyLinkDropdownItem = () => findDropdownItems().at(1); + const findDeleteDropdownItem = () => findDropdownItems().at(2); + + const showToast = jest.fn(); function createComponent({ props = {}, data = { isEditing: false }, mountFn = mountExtended, mocks = { + $toast: { + show: showToast, + }, $route, $apollo: { mutate: jest.fn().mockResolvedValue({ data: { updateNote: {} } }), @@ -239,6 +245,7 @@ describe('Design note component', () => { expect(findDropdown().exists()).toBe(true); expect(findEditDropdownItem().exists()).toBe(true); + expect(findCopyLinkDropdownItem().exists()).toBe(true); expect(findDeleteDropdownItem().exists()).toBe(true); expect(findDropdown().props('items')[0].extraAttrs.class).toBe('gl-sm-display-none!'); }); @@ -266,6 +273,40 @@ describe('Design note component', () => { expect(wrapper.emitted()).toEqual({ 'delete-note': [[{ ...payload }]] }); }); + it('shows a success toast after copying the url to the clipboard', () => { + createComponent({ + props: { + note: { + ...note, + userPermissions: { + adminNote: true, + }, + }, + }, + }); + + findCopyLinkDropdownItem().find('button').trigger('click'); + + expect(showToast).toHaveBeenCalledWith('Link copied to clipboard.'); + }); + + it('has data-clipboard-text set to the correct url', () => { + createComponent({ + props: { + note: { + ...note, + userPermissions: { + adminNote: true, + }, + }, + }, + }); + + expect(findCopyLinkDropdownItem().props('item').extraAttrs['data-clipboard-text']).toBe( + 'http://test.host/#note_123', + ); + }); + describe('when user has award emoji permissions', () => { const findEmojiPicker = () => wrapper.findComponent(EmojiPicker); const propsData = { diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js index e508045eff3..d0c2a1a5f1b 100644 --- a/spec/frontend/issues/show/components/header_actions_spec.js +++ b/spec/frontend/issues/show/components/header_actions_spec.js @@ -1,9 +1,16 @@ import Vue, { nextTick } from 'vue'; -import { GlDropdown, GlDropdownItem, GlLink, GlModal, GlButton } from '@gitlab/ui'; +import { + GlDisclosureDropdown, + GlDisclosureDropdownItem, + GlLink, + GlModal, + GlButton, +} from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; import VueApollo from 'vue-apollo'; +import { stubComponent } from 'helpers/stub_component'; import waitForPromises from 'helpers/wait_for_promises'; import { mockTracking } from 'helpers/tracking_helper'; import { createAlert, VARIANT_SUCCESS } from '~/alert'; @@ -120,8 +127,10 @@ describe('HeaderActions component', () => { const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`); const findMobileDropdown = () => findDropdownBy('mobile-dropdown'); const findDesktopDropdown = () => findDropdownBy('desktop-dropdown'); - const findMobileDropdownItems = () => findMobileDropdown().findAllComponents(GlDropdownItem); - const findDesktopDropdownItems = () => findDesktopDropdown().findAllComponents(GlDropdownItem); + const findMobileDropdownItems = () => + findMobileDropdown().findAllComponents(GlDisclosureDropdownItem); + const findDesktopDropdownItems = () => + findDesktopDropdown().findAllComponents(GlDisclosureDropdownItem); const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector); const findReportAbuseButton = () => wrapper.find(`[data-testid="report-abuse-item"]`); const findNotificationWidget = () => wrapper.find(`[data-testid="notification-toggle"]`); @@ -179,6 +188,11 @@ describe('HeaderActions component', () => { }, stubs: { GlButton, + GlDisclosureDropdown: stubComponent(GlDisclosureDropdown, { + methods: { + close: jest.fn(), + }, + }), }, }); }; @@ -217,7 +231,7 @@ describe('HeaderActions component', () => { }); it('calls apollo mutation', () => { - findToggleIssueStateButton().vm.$emit('click'); + findToggleIssueStateButton().vm.$emit('action'); expect(updateIssueMutationResponseHandler).toHaveBeenCalledWith({ input: { @@ -229,7 +243,7 @@ describe('HeaderActions component', () => { }); it('dispatches a custom event to update the issue page', async () => { - findToggleIssueStateButton().vm.$emit('click'); + findToggleIssueStateButton().vm.$emit('action'); await waitForPromises(); @@ -286,7 +300,11 @@ describe('HeaderActions component', () => { it(`${isItemVisible ? 'shows' : 'hides'} "${itemText}" item`, () => { expect( findDropdownItems() - .filter((item) => item.text() === itemText) + .filter((item) => { + return item.props('item') + ? item.props('item').text === itemText + : item.text() === itemText; + }) .exists(), ).toBe(isItemVisible); }); @@ -313,7 +331,7 @@ describe('HeaderActions component', () => { it('should trigger "open.form" event when clicked', async () => { expect(issuesEventHub.$emit).not.toHaveBeenCalled(); - await findEditButton().trigger('click'); + await findEditButton().vm.$emit('click'); expect(issuesEventHub.$emit).toHaveBeenCalledWith('open.form'); }); }); @@ -328,7 +346,7 @@ describe('HeaderActions component', () => { }); it('tracks clicking on button', () => { - findDesktopDropdownItems().at(4).vm.$emit('click'); + findDesktopDropdownItems().at(4).vm.$emit('action'); expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_dropdown', { label: 'delete_issue', @@ -345,7 +363,7 @@ describe('HeaderActions component', () => { promoteToEpicHandler: promoteToEpicMutationSuccessResponseHandler, }); - wrapper.find('[data-testid="promote-button"]').vm.$emit('click'); + wrapper.find('[data-testid="promote-button"]').vm.$emit('action'); await waitForPromises(); }); @@ -381,7 +399,7 @@ describe('HeaderActions component', () => { promoteToEpicHandler: promoteToEpicMutationErrorHandler, }); - wrapper.find('[data-testid="promote-button"]').vm.$emit('click'); + wrapper.find('[data-testid="promote-button"]').vm.$emit('action'); await waitForPromises(); }); @@ -483,7 +501,7 @@ describe('HeaderActions component', () => { }); it('opens the abuse category drawer', async () => { - findReportAbuseButton().vm.$emit('click'); + findReportAbuseButton().vm.$emit('action'); await nextTick(); @@ -491,7 +509,7 @@ describe('HeaderActions component', () => { }); it('closes the abuse category drawer', async () => { - await findReportAbuseButton().vm.$emit('click'); + await findReportAbuseButton().vm.$emit('action'); expect(findAbuseCategorySelector().exists()).toEqual(true); await findAbuseCategorySelector().vm.$emit('close-drawer'); @@ -603,7 +621,7 @@ describe('HeaderActions component', () => { }); it('shows toast message', () => { - findCopyRefenceDropdownItem().vm.$emit('click'); + findCopyRefenceDropdownItem().vm.$emit('action'); expect(toast).toHaveBeenCalledWith('Reference copied'); }); @@ -652,7 +670,7 @@ describe('HeaderActions component', () => { }); it('shows toast message', () => { - findCopyEmailItem().vm.$emit('click'); + findCopyEmailItem().vm.$emit('action'); expect(toast).toHaveBeenCalledWith('Email address copied'); }); @@ -710,11 +728,13 @@ describe('HeaderActions component', () => { props: { issueType, issuableEmailAddress: 'mock-email-address' }, }); - expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe( + expect(wrapper.findComponent(GlDisclosureDropdown).props('toggleText')).toBe( `${capitalizeFirstCharacter(expectedText)} actions`, ); expect(findDropdownBy('copy-email').text()).toBe(`Copy ${expectedText} email address`); - expect(findDesktopDropdownItems().at(1).text()).toBe(`New related ${expectedText}`); + expect(findDesktopDropdownItems().at(1).props('item').text).toBe( + `New related ${expectedText}`, + ); }); }); }); diff --git a/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js b/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js index 56c915c4cae..f049001ba45 100644 --- a/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js +++ b/spec/frontend/sidebar/components/move/issuable_move_dropdown_spec.js @@ -1,19 +1,9 @@ -import { nextTick } from 'vue'; -import { - GlIcon, - GlLoadingIcon, - GlDropdown, - GlDropdownForm, - GlDropdownItem, - GlSearchBoxByType, - GlButton, -} from '@gitlab/ui'; +import { GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import axios from '~/lib/utils/axios_utils'; import IssuableMoveDropdown from '~/sidebar/components/move/issuable_move_dropdown.vue'; -import { stubComponent } from 'helpers/stub_component'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; const mockProjects = [ @@ -42,318 +32,136 @@ const mockProps = { disabled: false, }; -const mockEvent = { - stopPropagation: jest.fn(), - preventDefault: jest.fn(), -}; - -const focusInputMock = jest.fn(); -const hideMock = jest.fn(); - describe('IssuableMoveDropdown', () => { let mock; let wrapper; - const createComponent = (propsData = mockProps) => { - wrapper = shallowMountExtended(IssuableMoveDropdown, { - propsData, - stubs: { - GlDropdown: stubComponent(GlDropdown, { - methods: { - hide: hideMock, - }, - }), - GlSearchBoxByType: stubComponent(GlSearchBoxByType, { - methods: { - focusInput: focusInputMock, - }, - }), - }, - }); + const createComponent = (propsData = {}) => { + wrapper = mountExtended(IssuableMoveDropdown, { propsData: { ...mockProps, ...propsData } }); }; beforeEach(() => { mock = new MockAdapter(axios); mock.onGet(mockProps.projectsFetchPath).reply(HTTP_STATUS_OK, mockProjects); - - createComponent(); }); afterEach(() => { mock.restore(); }); - const findCollapsedEl = () => wrapper.findByTestId('move-collapsed'); - const findFooter = () => wrapper.findByTestId('footer'); - const findHeader = () => wrapper.findByTestId('header'); - const findFailedLoadResults = () => wrapper.findByTestId('failed-load-results'); - const findDropdownContent = () => wrapper.findByTestId('content'); - const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); - const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findDropdownEl = () => wrapper.findComponent(GlDropdown); - const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); - - describe('watch', () => { - describe('searchKey', () => { - it('calls `fetchProjects` with value of the prop', async () => { - jest.spyOn(axios, 'get'); - findSearchBox().vm.$emit('input', 'foo'); - - await waitForPromises(); - - expect(axios.get).toHaveBeenCalledWith('/-/autocomplete/projects?project_id=1', { - params: { search: 'foo' }, - }); - }); - }); - }); - - describe('methods', () => { - describe('fetchProjects', () => { - it('sets projectsListLoading to true and projectsListLoadFailed to false', async () => { - findDropdownEl().vm.$emit('shown'); - await nextTick(); - - expect(findLoadingIcon().exists()).toBe(true); - expect(findFailedLoadResults().exists()).toBe(false); - }); - - it('calls `axios.get` with `projectsFetchPath` and query param `search`', async () => { - jest.spyOn(axios, 'get'); - - findSearchBox().vm.$emit('input', 'foo'); - await waitForPromises(); - - expect(axios.get).toHaveBeenCalledWith( - mockProps.projectsFetchPath, - expect.objectContaining({ - params: { - search: 'foo', - }, - }), - ); - }); - - it('sets response to `projects` and focuses on searchInput when request is successful', async () => { - jest.spyOn(axios, 'get'); - - findSearchBox().vm.$emit('input', 'foo'); - await waitForPromises(); + const findDropdownButton = () => wrapper.findByTestId('dropdown-button'); + const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox); + const findDropdownMoveButton = () => wrapper.findByTestId('dropdown-move-button'); + const findDropdownItemsText = () => + wrapper.findAllComponents(GlListboxItem).wrappers.map((item) => item.text()); - expect(findAllDropdownItems()).toHaveLength(mockProjects.length); - expect(focusInputMock).toHaveBeenCalled(); - }); - - it('sets projectsListLoadFailed to true when request fails', async () => { - jest.spyOn(axios, 'get').mockRejectedValue({}); - - findSearchBox().vm.$emit('input', 'foo'); - await waitForPromises(); - - expect(findFailedLoadResults().exists()).toBe(true); - }); - - it('sets projectsListLoading to false when request completes', async () => { - jest.spyOn(axios, 'get'); - - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); - - expect(findLoadingIcon().exists()).toBe(false); - }); - }); - - describe('isSelectedProject', () => { - it.each` - projectIndex | selectedProjectIndex | title | returnValue - ${0} | ${0} | ${'are same projects'} | ${true} - ${0} | ${1} | ${'are different projects'} | ${false} - `( - 'returns $returnValue when selectedProject and provided project param $title', - async ({ projectIndex, selectedProjectIndex, returnValue }) => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); - - findAllDropdownItems().at(selectedProjectIndex).vm.$emit('click', mockEvent); - - await nextTick(); - - expect(findAllDropdownItems().at(projectIndex).props('isChecked')).toBe(returnValue); - }, - ); - - it('returns false when selectedProject is null', async () => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + it('renders a dropdown button with provided title and header', () => { + createComponent(); - expect(findAllDropdownItems().at(0).props('isChecked')).toBe(false); - }); - }); + expect(findDropdownButton().text()).toBe(mockProps.dropdownButtonTitle); + expect(findDropdown().props('headerText')).toBe(mockProps.dropdownHeaderTitle); }); - describe('template', () => { - it('renders collapsed state element with icon', () => { - const collapsedEl = findCollapsedEl(); - - expect(collapsedEl.exists()).toBe(true); - expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle); - expect(collapsedEl.findComponent(GlIcon).exists()).toBe(true); - expect(collapsedEl.findComponent(GlIcon).props('name')).toBe('arrow-right'); - }); - - describe('gl-dropdown component', () => { - it('renders component container element', () => { - expect(findDropdownEl().exists()).toBe(true); - expect(findDropdownEl().props('block')).toBe(true); - }); + it('renders the dropdown button as disabled when disabled prop is true', () => { + createComponent({ disabled: true }); - it('renders gl-dropdown-form component', () => { - expect(findDropdownEl().findComponent(GlDropdownForm).exists()).toBe(true); - }); - - it('renders disabled dropdown when `disabled` is true', () => { - createComponent({ ...mockProps, disabled: true }); - expect(findDropdownEl().props('disabled')).toBe(true); - }); - - it('renders header element', () => { - const headerEl = findHeader(); - - expect(headerEl.exists()).toBe(true); - expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle); - expect(headerEl.findComponent(GlButton).props('icon')).toBe('close'); - }); - - it('renders gl-search-box-by-type component', () => { - const searchEl = findDropdownEl().findComponent(GlSearchBoxByType); - - expect(searchEl.exists()).toBe(true); - expect(searchEl.attributes()).toMatchObject({ - placeholder: 'Search project', - debounce: '300', - }); - }); - - it('renders gl-loading-icon component when projectsListLoading prop is true', async () => { - findDropdownEl().vm.$emit('shown'); - await nextTick(); - - expect(findLoadingIcon().exists()).toBe(true); - }); - - it('renders gl-dropdown-item components for available projects', async () => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); - - findAllDropdownItems().at(0).vm.$emit('click', mockEvent); - await nextTick(); - - expect(findAllDropdownItems()).toHaveLength(mockProjects.length); - expect(findAllDropdownItems().at(0).props()).toMatchObject({ - isCheckItem: true, - isChecked: true, - }); - expect(findAllDropdownItems().at(0).text()).toBe(mockProjects[0].name_with_namespace); - }); - - it('renders string "No matching results" when search does not yield any matches', async () => { - mock.onGet(mockProps.projectsFetchPath).reply(HTTP_STATUS_OK, []); + expect(findDropdownButton().props('disabled')).toBe(true); + }); - findSearchBox().vm.$emit('input', 'foo'); - await waitForPromises(); + it('triggers a project search when dropdown button is clicked', async () => { + createComponent(); - expect(findDropdownContent().text()).toContain('No matching results'); - }); + await findDropdownButton().trigger('click'); + await waitForPromises(); - it('renders string "Failed to load projects" when loading projects list fails', async () => { - mock.onGet(mockProps.projectsFetchPath).reply(HTTP_STATUS_OK, []); - jest.spyOn(axios, 'get').mockRejectedValue({}); + expect(mock.history.get).toHaveLength(1); - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + expect(findDropdownItemsText()).toEqual([ + 'Gitlab Org / Gitlab Shell', + 'Gnuwget / Wget2', + 'Commit451 / Lab Coat', + ]); + }); - expect(findDropdownContent().text()).toContain('Failed to load projects'); - }); + it('shows "No matching results" when no projects are found', async () => { + createComponent(); - it('renders gl-button within footer', async () => { - const moveButtonEl = findFooter().findComponent(GlButton); + mock.onGet(mockProps.projectsFetchPath).reply(HTTP_STATUS_OK, []); - expect(moveButtonEl.text()).toBe('Move'); - expect(moveButtonEl.attributes('disabled')).toBeDefined(); + await findDropdown().vm.$emit('search', 'foobar'); + await waitForPromises(); - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + expect(findDropdown().text()).toContain('No matching results'); + expect(findDropdownItemsText()).toEqual([]); + }); - findAllDropdownItems().at(0).vm.$emit('click', mockEvent); - await nextTick(); + it('shows "Failed to load projects" when request fails', async () => { + createComponent(); - expect(findFooter().findComponent(GlButton).attributes('disabled')).not.toBeDefined(); - }); - }); + mock.onGet(mockProps.projectsFetchPath).networkError(); - describe('events', () => { - it('collapsed state element emits `toggle-collapse` event on component when clicked', () => { - findCollapsedEl().trigger('click'); + await findDropdown().vm.$emit('search', 'foobar'); + await waitForPromises(); - expect(wrapper.emitted('toggle-collapse')).toHaveLength(1); - }); + expect(findDropdown().text()).toContain('Failed to load projects'); + expect(findDropdownItemsText()).toEqual([]); + }); - it('gl-dropdown component calls `fetchProjects` on `shown` event', () => { - jest.spyOn(axios, 'get'); + it('disables the Move issuable button if no project is selected', async () => { + createComponent(); - findDropdownEl().vm.$emit('shown'); + await findDropdownButton().trigger('click'); + await waitForPromises(); - expect(axios.get).toHaveBeenCalled(); - }); + expect(findDropdownMoveButton().props('disabled')).toBe(true); + }); - it('gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true', async () => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + it('shows search results when search is successful', async () => { + createComponent(); - findAllDropdownItems().at(0).vm.$emit('click', mockEvent); - await nextTick(); + mock.onGet(mockProps.projectsFetchPath).reply(HTTP_STATUS_OK, [ + { + id: 2, + name_with_namespace: 'Gitlab Org / Gitlab Shell', + full_path: 'gitlab-org/gitlab-shell', + }, + ]); - findDropdownEl().vm.$emit('hide', mockEvent); + await findDropdown().vm.$emit('search', 'shell'); + await waitForPromises(); - expect(mockEvent.preventDefault).toHaveBeenCalled(); - }); + expect(findDropdownItemsText()).toEqual(['Gitlab Org / Gitlab Shell']); + }); - it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', () => { - findDropdownEl().vm.$emit('hide'); + it('emits "move-issuable" event when Move issuable button is clicked', async () => { + createComponent(); - expect(wrapper.emitted('dropdown-close')).toHaveLength(1); - }); + await findDropdownButton().trigger('click'); + await waitForPromises(); - it('close icon in dropdown header closes the dropdown when clicked', async () => { - findHeader().findComponent(GlButton).vm.$emit('click', mockEvent); + await wrapper.findAllComponents(GlListboxItem).wrappers[0].trigger('click'); + await findDropdownMoveButton().trigger('click'); - await nextTick(); - expect(hideMock).toHaveBeenCalled(); - }); + expect(wrapper.emitted('move-issuable')).toEqual([[mockProjects[0]]]); + }); - it('sets project for clicked gl-dropdown-item to selectedProject', async () => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + it('disables the Move issuable button when moveInProgress prop is true', async () => { + createComponent({ moveInProgress: true }); - findAllDropdownItems().at(0).vm.$emit('click', mockEvent); - await nextTick(); + await findDropdownButton().trigger('click'); + await waitForPromises(); - expect(findAllDropdownItems().at(0).props('isChecked')).toBe(true); - }); + expect(findDropdownMoveButton().props('disabled')).toBe(true); + }); - it('hides dropdown and emits `move-issuable` event when move button is clicked', async () => { - findDropdownEl().vm.$emit('shown'); - await waitForPromises(); + it('emits "dropdown-close" event when dropdown is hidden', async () => { + createComponent(); - findAllDropdownItems().at(0).vm.$emit('click', mockEvent); - await nextTick(); + await findDropdownButton().trigger('click'); + await waitForPromises(); - findFooter().findComponent(GlButton).vm.$emit('click'); + await findDropdown().vm.$emit('hidden'); - expect(hideMock).toHaveBeenCalled(); - expect(wrapper.emitted('move-issuable')).toHaveLength(1); - expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]); - }); - }); + expect(wrapper.emitted('dropdown-close')).toHaveLength(1); }); }); diff --git a/spec/frontend/work_items/components/work_item_milestone_spec.js b/spec/frontend/work_items/components/work_item_milestone_spec.js index e303ad4b481..fc2c5eb2af2 100644 --- a/spec/frontend/work_items/components/work_item_milestone_spec.js +++ b/spec/frontend/work_items/components/work_item_milestone_spec.js @@ -1,14 +1,8 @@ -import { - GlDropdown, - GlDropdownItem, - GlSearchBoxByType, - GlSkeletonLoader, - GlFormGroup, - GlDropdownText, -} from '@gitlab/ui'; +import { GlCollapsibleListbox, GlListboxItem, GlSkeletonLoader, GlFormGroup } from '@gitlab/ui'; + import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -import WorkItemMilestone from '~/work_items/components/work_item_milestone.vue'; +import WorkItemMilestone, { noMilestoneId } from '~/work_items/components/work_item_milestone.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import { mockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; @@ -32,17 +26,13 @@ describe('WorkItemMilestone component', () => { const workItemId = 'gid://gitlab/WorkItem/1'; const workItemType = 'Task'; - const findDropdown = () => wrapper.findComponent(GlDropdown); - const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); + const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox); const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); - const findNoMilestoneDropdownItem = () => wrapper.findByTestId('no-milestone'); - const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); - const findFirstDropdownItem = () => findDropdownItems().at(0); - const findDropdownTexts = () => wrapper.findAllComponents(GlDropdownText); - const findDropdownItemAtIndex = (index) => findDropdownItems().at(index); + const findNoMilestoneDropdownItem = () => wrapper.findByTestId('listbox-item-no-milestone-id'); + const findDropdownItems = () => wrapper.findAllComponents(GlListboxItem); const findDisabledTextSpan = () => wrapper.findByTestId('disabled-text'); - const findDropdownTextAtIndex = (index) => findDropdownTexts().at(index); const findInputGroup = () => wrapper.findComponent(GlFormGroup); + const findNoResultsText = () => wrapper.findByTestId('no-results-text'); const successSearchQueryHandler = jest.fn().mockResolvedValue(projectMilestonesResponse); const successSearchWithNoMatchingMilestones = jest @@ -74,8 +64,7 @@ describe('WorkItemMilestone component', () => { workItemType, }, stubs: { - GlDropdown, - GlSearchBoxByType, + GlCollapsibleListbox, }, }); }; @@ -106,7 +95,7 @@ describe('WorkItemMilestone component', () => { it(`has a value of "Add to milestone"`, () => { createComponent({ canUpdate: true, milestone: null }); - expect(findDropdown().props('text')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER); + expect(findDropdown().props('toggleText')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER); }); }); @@ -114,7 +103,7 @@ describe('WorkItemMilestone component', () => { it('has the search box', () => { createComponent(); - expect(findSearchBox().exists()).toBe(true); + expect(findDropdown().props('searchable')).toBe(true); }); it('shows no matching results when no items', () => { @@ -122,9 +111,8 @@ describe('WorkItemMilestone component', () => { searchQueryHandler: successSearchWithNoMatchingMilestones, }); - expect(findDropdownTextAtIndex(0).text()).toBe(WorkItemMilestone.i18n.NO_MATCHING_RESULTS); + expect(findNoResultsText().text()).toBe(WorkItemMilestone.i18n.NO_MATCHING_RESULTS); expect(findDropdownItems()).toHaveLength(1); - expect(findDropdownTexts()).toHaveLength(1); }); }); @@ -165,16 +153,18 @@ describe('WorkItemMilestone component', () => { it('changes the milestone to null when clicked on no milestone', async () => { showDropdown(); - findFirstDropdownItem().vm.$emit('click'); + findDropdown().vm.$emit('select', noMilestoneId); hideDropdown(); await nextTick(); expect(findDropdown().props('loading')).toBe(true); await waitForPromises(); - - expect(findDropdown().props('loading')).toBe(false); - expect(findDropdown().props('text')).toBe(WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER); + expect(findDropdown().props()).toMatchObject({ + loading: false, + toggleText: WorkItemMilestone.i18n.MILESTONE_PLACEHOLDER, + toggleClass: expect.arrayContaining(['gl-text-gray-500!']), + }); }); it('changes the milestone to the selected milestone', async () => { @@ -182,15 +172,16 @@ describe('WorkItemMilestone component', () => { /** the index is -1 since no matching results is also a dropdown item */ const milestoneAtIndex = projectMilestonesResponse.data.workspace.attributes.nodes[milestoneIndex - 1]; + showDropdown(); await waitForPromises(); - findDropdownItemAtIndex(milestoneIndex).vm.$emit('click'); + findDropdown().vm.$emit('select', milestoneAtIndex.id); hideDropdown(); await waitForPromises(); - expect(findDropdown().props('text')).toBe(milestoneAtIndex.title); + expect(findDropdown().props('toggleText')).toBe(milestoneAtIndex.title); }); }); @@ -208,7 +199,7 @@ describe('WorkItemMilestone component', () => { }); showDropdown(); - findFirstDropdownItem().vm.$emit('click'); + findDropdown().vm.$emit('select', noMilestoneId); hideDropdown(); await waitForPromises(); @@ -224,7 +215,7 @@ describe('WorkItemMilestone component', () => { createComponent({ canUpdate: true }); showDropdown(); - findFirstDropdownItem().vm.$emit('click'); + findDropdown().vm.$emit('select', noMilestoneId); hideDropdown(); await waitForPromises(); diff --git a/spec/frontend/work_items/components/work_item_parent_spec.js b/spec/frontend/work_items/components/work_item_parent_spec.js index a977e7d396f..c60620d210a 100644 --- a/spec/frontend/work_items/components/work_item_parent_spec.js +++ b/spec/frontend/work_items/components/work_item_parent_spec.js @@ -87,7 +87,6 @@ describe('WorkItemParent component', () => { headerText: 'Assign parent', category: 'tertiary', loading: false, - noCaret: true, isCheckCentered: true, searchable: true, searching: false, @@ -96,7 +95,6 @@ describe('WorkItemParent component', () => { toggleText: 'None', searchPlaceholder: 'Search', resetButtonLabel: 'Unassign', - block: true, }); }); @@ -104,14 +102,12 @@ describe('WorkItemParent component', () => { createComponent({ canUpdate: false, parent: mockParentWidgetResponse }); expect(findCollapsibleListbox().exists()).toBe(false); - expect(findParentText().exists()).toBe(true); expect(findParentText().text()).toBe('Objective 101'); }); it('shows loading while searching', async () => { await findCollapsibleListbox().vm.$emit('shown'); expect(findCollapsibleListbox().props('searching')).toBe(true); - expect(findCollapsibleListbox().props('no-caret')).toBeUndefined(); }); }); @@ -170,7 +166,6 @@ describe('WorkItemParent component', () => { describe('listbox', () => { const selectWorkItem = async (workItem) => { - await findCollapsibleListbox().vm.$emit('shown'); await findCollapsibleListbox().vm.$emit('select', workItem); }; diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index 997f9e6eac7..14f99f144b2 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -23,8 +23,8 @@ RSpec.describe EnvironmentsHelper, feature_category: :environment_management do 'settings_path' => edit_project_settings_integration_path(project, 'prometheus'), 'clusters_path' => project_clusters_path(project), 'current_environment_name' => environment.name, - 'documentation_path' => help_page_path('administration/monitoring/prometheus/index.md'), - 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), + 'documentation_path' => help_page_path('administration/monitoring/prometheus/index'), + 'add_dashboard_documentation_path' => help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'), 'empty_getting_started_svg_path' => match_asset_path('/assets/illustrations/monitoring/getting_started.svg'), 'empty_loading_svg_path' => match_asset_path('/assets/illustrations/monitoring/loading.svg'), 'empty_no_data_svg_path' => match_asset_path('/assets/illustrations/monitoring/no_data.svg'), diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index 47500b8e21e..d5d7f8f72b3 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -101,7 +101,7 @@ RSpec.describe IdeHelper, feature_category: :web_ide do 'user-preferences-path' => profile_preferences_path, 'sign-in-path' => 'test-sign-in-path', 'new-web-ide-help-page-path' => - help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'), + help_page_path('user/project/web_ide/index', anchor: 'vscode-reimplementation'), 'csp-nonce' => 'test-csp-nonce', 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path') } diff --git a/spec/helpers/operations_helper_spec.rb b/spec/helpers/operations_helper_spec.rb index 9e50712a386..e018ab41a83 100644 --- a/spec/helpers/operations_helper_spec.rb +++ b/spec/helpers/operations_helper_spec.rb @@ -30,7 +30,7 @@ RSpec.describe OperationsHelper do it 'returns the correct values' do expect(subject).to eq( - 'alerts_setup_url' => help_page_path('operations/incident_management/integrations.md', anchor: 'configuration'), + 'alerts_setup_url' => help_page_path('operations/incident_management/integrations', anchor: 'configuration'), 'alerts_usage_url' => project_alert_management_index_path(project), 'prometheus_form_path' => project_settings_integration_path(project, prometheus_integration), 'prometheus_reset_key_path' => reset_alerting_token_project_settings_operations_path(project), diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb index 213dee0d19d..f70b38377d8 100644 --- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb +++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb @@ -422,11 +422,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m describe '#create_batched_job!' do let(:batched_migration) do - create(:batched_background_migration, - batch_size: 999, - sub_batch_size: 99, - pause_ms: 250 - ) + create( + :batched_background_migration, + batch_size: 999, + sub_batch_size: 99, + pause_ms: 250 + ) end it 'creates a batched_job with the correct batch configuration' do diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb index 8d74d16f4e5..bbcb65109ce 100644 --- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb +++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb @@ -32,17 +32,17 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, ' end it 'runs the migration job' do - expect(job_class).to receive(:new) - .with(start_id: 1, - end_id: 10, - batch_table: 'events', - batch_column: 'id', - sub_batch_size: 1, - pause_ms: pause_ms, - job_arguments: active_migration.job_arguments, - connection: connection, - sub_batch_exception: sub_batch_exception) - .and_return(job_instance) + expect(job_class).to receive(:new).with( + start_id: 1, + end_id: 10, + batch_table: 'events', + batch_column: 'id', + sub_batch_size: 1, + pause_ms: pause_ms, + job_arguments: active_migration.job_arguments, + connection: connection, + sub_batch_exception: sub_batch_exception + ).and_return(job_instance) expect(job_instance).to receive(:perform).with(no_args) diff --git a/spec/lib/gitlab/database/background_migration/prometheus_metrics_spec.rb b/spec/lib/gitlab/database/background_migration/prometheus_metrics_spec.rb index 1f256de35ec..8f380a8229c 100644 --- a/spec/lib/gitlab/database/background_migration/prometheus_metrics_spec.rb +++ b/spec/lib/gitlab/database/background_migration/prometheus_metrics_spec.rb @@ -5,11 +5,14 @@ require 'spec_helper' RSpec.describe Gitlab::Database::BackgroundMigration::PrometheusMetrics, :prometheus do describe '#track' do let(:job_record) do - build(:batched_background_migration_job, :succeeded, - started_at: Time.current - 2.minutes, - finished_at: Time.current - 1.minute, - updated_at: Time.current, - metrics: { 'timings' => { 'update_all' => [0.05, 0.2, 0.4, 0.9, 4] } }) + build( + :batched_background_migration_job, + :succeeded, + started_at: Time.current - 2.minutes, + finished_at: Time.current - 1.minute, + updated_at: Time.current, + metrics: { 'timings' => { 'update_all' => [0.05, 0.2, 0.4, 0.9, 4] } } + ) end let(:labels) { job_record.batched_migration.prometheus_labels } diff --git a/spec/lib/gitlab/database/bulk_update_spec.rb b/spec/lib/gitlab/database/bulk_update_spec.rb index fa519cffd6b..2f0859dba74 100644 --- a/spec/lib/gitlab/database/bulk_update_spec.rb +++ b/spec/lib/gitlab/database/bulk_update_spec.rb @@ -92,7 +92,7 @@ RSpec.describe Gitlab::Database::BulkUpdate do end context 'validates prepared_statements support', :reestablished_active_record_base, - :suppress_gitlab_schemas_validate_connection do + :suppress_gitlab_schemas_validate_connection do using RSpec::Parameterized::TableSyntax where(:prepared_statements) do diff --git a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb index 552df64096a..1824a50cb28 100644 --- a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb +++ b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb @@ -8,14 +8,14 @@ RSpec.describe Gitlab::Database::LooseForeignKeys do it 'all definitions have assigned a known gitlab_schema and on_delete' do is_expected.to all(have_attributes( - options: a_hash_including( - column: be_a(String), - gitlab_schema: be_in(Gitlab::Database.schemas_to_base_models.symbolize_keys.keys), - on_delete: be_in([:async_delete, :async_nullify]) - ), - from_table: be_a(String), - to_table: be_a(String) - )) + options: a_hash_including( + column: be_a(String), + gitlab_schema: be_in(Gitlab::Database.schemas_to_base_models.symbolize_keys.keys), + on_delete: be_in([:async_delete, :async_nullify]) + ), + from_table: be_a(String), + to_table: be_a(String) + )) end context 'ensure keys are sorted' do diff --git a/spec/lib/gitlab/database/migrations/constraints_helpers_spec.rb b/spec/lib/gitlab/database/migrations/constraints_helpers_spec.rb index 476b5f3a784..4d7c51a3fbf 100644 --- a/spec/lib/gitlab/database/migrations/constraints_helpers_spec.rb +++ b/spec/lib/gitlab/database/migrations/constraints_helpers_spec.rb @@ -13,9 +13,11 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#check_constraint_name' do it 'returns a valid constraint name' do - name = model.check_constraint_name(:this_is_a_very_long_table_name, - :with_a_very_long_column_name, - :with_a_very_long_type) + name = model.check_constraint_name( + :this_is_a_very_long_table_name, + :with_a_very_long_column_name, + :with_a_very_long_type + ) expect(name).to be_an_instance_of(String) expect(name).to start_with('check_') @@ -404,9 +406,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#add_text_limit' do context 'when it is called with the default options' do it 'calls add_check_constraint with an infered constraint name and validate: true' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'max_length') + constraint_name = model.check_constraint_name(:test_table, :name, 'max_length') check = "char_length(name) <= 255" expect(model).to receive(:check_constraint_name).and_call_original @@ -440,9 +440,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#validate_text_limit' do context 'when constraint_name is not provided' do it 'calls validate_check_constraint with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'max_length') + constraint_name = model.check_constraint_name(:test_table, :name, 'max_length') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:validate_check_constraint) @@ -468,9 +466,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#remove_text_limit' do context 'when constraint_name is not provided' do it 'calls remove_check_constraint with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'max_length') + constraint_name = model.check_constraint_name(:test_table, :name, 'max_length') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:remove_check_constraint) @@ -496,9 +492,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#check_text_limit_exists?' do context 'when constraint_name is not provided' do it 'calls check_constraint_exists? with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'max_length') + constraint_name = model.check_constraint_name(:test_table, :name, 'max_length') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:check_constraint_exists?) @@ -524,9 +518,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#add_not_null_constraint' do context 'when it is called with the default options' do it 'calls add_check_constraint with an infered constraint name and validate: true' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'not_null') + constraint_name = model.check_constraint_name(:test_table, :name, 'not_null') check = "name IS NOT NULL" expect(model).to receive(:column_is_nullable?).and_return(true) @@ -571,9 +563,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#validate_not_null_constraint' do context 'when constraint_name is not provided' do it 'calls validate_check_constraint with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'not_null') + constraint_name = model.check_constraint_name(:test_table, :name, 'not_null') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:validate_check_constraint) @@ -599,9 +589,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#remove_not_null_constraint' do context 'when constraint_name is not provided' do it 'calls remove_check_constraint with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'not_null') + constraint_name = model.check_constraint_name(:test_table, :name, 'not_null') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:remove_check_constraint) @@ -627,9 +615,7 @@ RSpec.describe Gitlab::Database::Migrations::ConstraintsHelpers do describe '#check_not_null_constraint_exists?' do context 'when constraint_name is not provided' do it 'calls check_constraint_exists? with an infered constraint name' do - constraint_name = model.check_constraint_name(:test_table, - :name, - 'not_null') + constraint_name = model.check_constraint_name(:test_table, :name, 'not_null') expect(model).to receive(:check_constraint_name).and_call_original expect(model).to receive(:check_constraint_exists?) diff --git a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb index a12e0909dc2..81368dde94d 100644 --- a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb +++ b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb @@ -75,8 +75,12 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do context 'on successful execution' do subject do - instrumentation.observe(version: migration_version, name: migration_name, - connection: connection, meta: migration_meta) {} + instrumentation.observe( + version: migration_version, + name: migration_name, + connection: connection, + meta: migration_meta + ) {} end it 'records a valid observation', :aggregate_failures do @@ -98,8 +102,12 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do with_them do subject(:observe) do - instrumentation.observe(version: migration_version, name: migration_name, - connection: connection, meta: migration_meta) { raise exception, error_message } + instrumentation.observe( + version: migration_version, + name: migration_name, + connection: connection, + meta: migration_meta + ) { raise exception, error_message } end context 'retrieving observations' do diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb index 31a194ae883..660c4347ffa 100644 --- a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb +++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb @@ -80,8 +80,11 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez end subject(:sample_migration) do - described_class.new(result_dir: result_dir, connection: connection, - from_id: from_id).run_jobs(for_duration: 1.minute) + described_class.new( + result_dir: result_dir, + connection: connection, + from_id: from_id + ).run_jobs(for_duration: 1.minute) end it 'runs sampled jobs from the batched background migration' do @@ -125,12 +128,19 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez calls << args end - queue_migration(migration_name, table_name, :id, - job_interval: 5.minutes, - batch_size: 100) + queue_migration( + migration_name, + table_name, + :id, + job_interval: 5.minutes, + batch_size: 100 + ) - described_class.new(result_dir: result_dir, connection: connection, - from_id: from_id).run_jobs(for_duration: 3.minutes) + described_class.new( + result_dir: result_dir, + connection: connection, + from_id: from_id + ).run_jobs(for_duration: 3.minutes) expect(calls).not_to be_empty end @@ -142,13 +152,19 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez calls << args end - queue_migration(migration_name, table_name, :id, - job_interval: 5.minutes, - batch_size: num_rows_in_table * 2, - sub_batch_size: num_rows_in_table * 2) + queue_migration( + migration_name, + table_name, :id, + job_interval: 5.minutes, + batch_size: num_rows_in_table * 2, + sub_batch_size: num_rows_in_table * 2 + ) - described_class.new(result_dir: result_dir, connection: connection, - from_id: from_id).run_jobs(for_duration: 3.minutes) + described_class.new( + result_dir: result_dir, + connection: connection, + from_id: from_id + ).run_jobs(for_duration: 3.minutes) expect(calls.size).to eq(1) end @@ -161,13 +177,20 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez calls << args end - queue_migration(migration_name, table_name, :id, - job_interval: 5.minutes, - batch_size: num_rows_in_table * 2, - sub_batch_size: num_rows_in_table * 2) - - described_class.new(result_dir: result_dir, connection: connection, - from_id: from_id).run_jobs(for_duration: 3.minutes) + queue_migration( + migration_name, + table_name, + :id, + job_interval: 5.minutes, + batch_size: num_rows_in_table * 2, + sub_batch_size: num_rows_in_table * 2 + ) + + described_class.new( + result_dir: result_dir, + connection: connection, + from_id: from_id + ).run_jobs(for_duration: 3.minutes) expect(calls.count).to eq(0) end @@ -181,26 +204,41 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez it 'runs all pending jobs based on the last migration id' do old_migration = define_background_migration(migration_name) - queue_migration(migration_name, table_name, :id, - job_interval: 5.minutes, - batch_size: 100) + queue_migration( + migration_name, + table_name, + :id, + job_interval: 5.minutes, + batch_size: 100 + ) last_id new_migration = define_background_migration('NewMigration') { travel 1.second } - queue_migration('NewMigration', table_name, :id, - job_interval: 5.minutes, - batch_size: 10, - sub_batch_size: 5) + queue_migration( + 'NewMigration', + table_name, + :id, + job_interval: 5.minutes, + batch_size: 10, + sub_batch_size: 5 + ) other_new_migration = define_background_migration('NewMigration2') { travel 2.seconds } - queue_migration('NewMigration2', table_name, :id, - job_interval: 5.minutes, - batch_size: 10, - sub_batch_size: 5) + queue_migration( + 'NewMigration2', + table_name, + :id, + job_interval: 5.minutes, + batch_size: 10, + sub_batch_size: 5 + ) expect_migration_runs(new_migration => 3, other_new_migration => 2, old_migration => 0) do - described_class.new(result_dir: result_dir, connection: connection, - from_id: last_id).run_jobs(for_duration: 5.seconds) + described_class.new( + result_dir: result_dir, + connection: connection, + from_id: last_id + ).run_jobs(for_duration: 5.seconds) end end end diff --git a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb index 04940028aee..eb78d836be0 100644 --- a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb +++ b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb @@ -57,17 +57,19 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do SQL end - Postgresql::DetachedPartition.create!(table_name: name, - drop_after: drop_after) + Postgresql::DetachedPartition.create!(table_name: name, drop_after: drop_after) end describe '#perform' do context 'when the partition should not be dropped yet' do it 'does not drop the partition' do - create_partition(name: :_test_partition, - from: 2.months.ago, to: 1.month.ago, - attached: false, - drop_after: 1.day.from_now) + create_partition( + name: :_test_partition, + from: 2.months.ago, + to: 1.month.ago, + attached: false, + drop_after: 1.day.from_now + ) dropper.perform @@ -77,11 +79,13 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do context 'with a partition to drop' do before do - create_partition(name: :_test_partition, - from: 2.months.ago, - to: 1.month.ago.beginning_of_month, - attached: false, - drop_after: 1.second.ago) + create_partition( + name: :_test_partition, + from: 2.months.ago, + to: 1.month.ago.beginning_of_month, + attached: false, + drop_after: 1.second.ago + ) end it 'drops the partition' do @@ -159,11 +163,13 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do context 'when the partition to drop is still attached to its table' do before do - create_partition(name: :_test_partition, - from: 2.months.ago, - to: 1.month.ago.beginning_of_month, - attached: true, - drop_after: 1.second.ago) + create_partition( + name: :_test_partition, + from: 2.months.ago, + to: 1.month.ago.beginning_of_month, + attached: true, + drop_after: 1.second.ago + ) end it 'does not drop the partition, but does remove the DetachedPartition entry' do @@ -192,17 +198,21 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do context 'with multiple partitions to drop' do before do - create_partition(name: :_test_partition_1, - from: 3.months.ago, - to: 2.months.ago, - attached: false, - drop_after: 1.second.ago) - - create_partition(name: :_test_partition_2, - from: 2.months.ago, - to: 1.month.ago, - attached: false, - drop_after: 1.second.ago) + create_partition( + name: :_test_partition_1, + from: 3.months.ago, + to: 2.months.ago, + attached: false, + drop_after: 1.second.ago + ) + + create_partition( + name: :_test_partition_2, + from: 2.months.ago, + to: 1.month.ago, + attached: false, + drop_after: 1.second.ago + ) end it 'drops both partitions' do diff --git a/spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb index 3afa338fdf7..8b18e5b6d08 100644 --- a/spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb +++ b/spec/lib/gitlab/database/partitioning/monthly_strategy_spec.rb @@ -235,8 +235,12 @@ RSpec.describe Gitlab::Database::Partitioning::MonthlyStrategy, feature_category subject { described_class.new(model, partitioning_key, retain_for: 3.months).extra_partitions } it 'prunes the unbounded partition ending 2020-05-01' do - min_value_to_may = Gitlab::Database::Partitioning::TimePartition.new(model.table_name, nil, '2020-05-01', - partition_name: '_test_partitioned_test_000000') + min_value_to_may = Gitlab::Database::Partitioning::TimePartition.new( + model.table_name, + nil, + '2020-05-01', + partition_name: '_test_partitioned_test_000000' + ) expect(subject).to contain_exactly(min_value_to_may) end @@ -247,8 +251,18 @@ RSpec.describe Gitlab::Database::Partitioning::MonthlyStrategy, feature_category it 'prunes the unbounded partition and the partition for May-June' do expect(subject).to contain_exactly( - Gitlab::Database::Partitioning::TimePartition.new(model.table_name, nil, '2020-05-01', partition_name: '_test_partitioned_test_000000'), - Gitlab::Database::Partitioning::TimePartition.new(model.table_name, '2020-05-01', '2020-06-01', partition_name: '_test_partitioned_test_202005') + Gitlab::Database::Partitioning::TimePartition.new( + model.table_name, + nil, + '2020-05-01', + partition_name: '_test_partitioned_test_000000' + ), + Gitlab::Database::Partitioning::TimePartition.new( + model.table_name, + '2020-05-01', + '2020-06-01', + partition_name: '_test_partitioned_test_202005' + ) ) end @@ -257,8 +271,18 @@ RSpec.describe Gitlab::Database::Partitioning::MonthlyStrategy, feature_category it 'prunes empty partitions' do expect(subject).to contain_exactly( - Gitlab::Database::Partitioning::TimePartition.new(model.table_name, nil, '2020-05-01', partition_name: '_test_partitioned_test_000000'), - Gitlab::Database::Partitioning::TimePartition.new(model.table_name, '2020-05-01', '2020-06-01', partition_name: '_test_partitioned_test_202005') + Gitlab::Database::Partitioning::TimePartition.new( + model.table_name, + nil, + '2020-05-01', + partition_name: '_test_partitioned_test_000000' + ), + Gitlab::Database::Partitioning::TimePartition.new( + model.table_name, + '2020-05-01', + '2020-06-01', + partition_name: '_test_partitioned_test_202005' + ) ) end diff --git a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb index ac4d345271e..9ca0a1b6e57 100644 --- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb +++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb @@ -15,9 +15,12 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy, feature_cate let(:detach_partition_if) { double('detach_partition_if') } subject(:strategy) do - described_class.new(model, :partition, - next_partition_if: next_partition_if, - detach_partition_if: detach_partition_if) + described_class.new( + model, + :partition, + next_partition_if: next_partition_if, + detach_partition_if: detach_partition_if + ) end before do @@ -213,9 +216,9 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy, feature_cate include PartitionedTable partitioned_by :partition, - strategy: :sliding_list, - next_partition_if: proc { false }, - detach_partition_if: proc { false } + strategy: :sliding_list, + next_partition_if: proc { false }, + detach_partition_if: proc { false } end end.to raise_error(/ignored_columns/) end @@ -228,9 +231,9 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy, feature_cate self.ignored_columns = [:partition] partitioned_by :partition, - strategy: :sliding_list, - next_partition_if: proc { false }, - detach_partition_if: proc { false } + strategy: :sliding_list, + next_partition_if: proc { false }, + detach_partition_if: proc { false } end end.not_to raise_error end @@ -255,9 +258,9 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy, feature_cate detach_partition?(...) end partitioned_by :partition, - strategy: :sliding_list, - next_partition_if: method(:next_partition_if_wrapper), - detach_partition_if: method(:detach_partition_if_wrapper) + strategy: :sliding_list, + next_partition_if: method(:next_partition_if_wrapper), + detach_partition_if: method(:detach_partition_if_wrapper) def self.next_partition?(current_partition); end diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb index a81c8a5a49c..aa644885306 100644 --- a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb +++ b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb @@ -99,8 +99,11 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do expect(migration).to receive(:add_index) .with(table_name, column_name, { name: index_name, where: 'x > 0', unique: true }) - migration.add_concurrent_partitioned_index(table_name, column_name, - { name: index_name, where: 'x > 0', unique: true }) + migration.add_concurrent_partitioned_index( + table_name, + column_name, + { name: index_name, where: 'x > 0', unique: true } + ) end end diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb index 6a947044317..182b6960384 100644 --- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb +++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb @@ -51,13 +51,15 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe end it 'delegates to a method on List::ConvertTable' do - expect_next_instance_of(Gitlab::Database::Partitioning::List::ConvertTable, - migration_context: migration, - table_name: source_table, - parent_table_name: partitioned_table, - partitioning_column: partition_column, - zero_partition_value: min_date, - **extra_options) do |converter| + expect_next_instance_of( + Gitlab::Database::Partitioning::List::ConvertTable, + migration_context: migration, + table_name: source_table, + parent_table_name: partitioned_table, + partitioning_column: partition_column, + zero_partition_value: min_date, + **extra_options + ) do |converter| expect(converter).to receive(expected_method) end @@ -70,11 +72,13 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe let(:lock_tables) { [source_table] } let(:expected_method) { :partition } let(:migrate) do - migration.convert_table_to_first_list_partition(table_name: source_table, - partitioning_column: partition_column, - parent_table_name: partitioned_table, - initial_partitioning_value: min_date, - lock_tables: lock_tables) + migration.convert_table_to_first_list_partition( + table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date, + lock_tables: lock_tables + ) end end end @@ -83,10 +87,12 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe it_behaves_like 'delegates to ConvertTable' do let(:expected_method) { :revert_partitioning } let(:migrate) do - migration.revert_converting_table_to_first_list_partition(table_name: source_table, - partitioning_column: partition_column, - parent_table_name: partitioned_table, - initial_partitioning_value: min_date) + migration.revert_converting_table_to_first_list_partition( + table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date + ) end end end @@ -95,11 +101,13 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe it_behaves_like 'delegates to ConvertTable' do let(:expected_method) { :prepare_for_partitioning } let(:migrate) do - migration.prepare_constraint_for_list_partitioning(table_name: source_table, - partitioning_column: partition_column, - parent_table_name: partitioned_table, - initial_partitioning_value: min_date, - async: false) + migration.prepare_constraint_for_list_partitioning( + table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date, + async: false + ) end end end @@ -108,10 +116,12 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe it_behaves_like 'delegates to ConvertTable' do let(:expected_method) { :revert_preparation_for_partitioning } let(:migrate) do - migration.revert_preparing_constraint_for_list_partitioning(table_name: source_table, - partitioning_column: partition_column, - parent_table_name: partitioned_table, - initial_partitioning_value: min_date) + migration.revert_preparing_constraint_for_list_partitioning( + table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date + ) end end end diff --git a/spec/lib/gitlab/database/postgres_constraint_spec.rb b/spec/lib/gitlab/database/postgres_constraint_spec.rb index 75084a69115..140180540e0 100644 --- a/spec/lib/gitlab/database/postgres_constraint_spec.rb +++ b/spec/lib/gitlab/database/postgres_constraint_spec.rb @@ -51,9 +51,11 @@ RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do subject(:check_constraints) { described_class.check_constraints.by_table_identifier(table_identifier) } it 'finds check constraints for the table' do - expect(check_constraints.map(&:name)).to contain_exactly(check_constraint_a_positive, - check_constraint_a_gt_b, - invalid_constraint_a) + expect(check_constraints.map(&:name)).to contain_exactly( + check_constraint_a_positive, + check_constraint_a_gt_b, + invalid_constraint_a + ) end it 'includes columns for the check constraints', :aggregate_failures do @@ -108,8 +110,12 @@ RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do describe '#including_column' do it 'only matches constraints on the given column' do constraints_on_a = described_class.by_table_identifier(table_identifier).including_column('a').map(&:name) - expect(constraints_on_a).to contain_exactly(check_constraint_a_positive, check_constraint_a_gt_b, - unique_constraint_a, invalid_constraint_a) + expect(constraints_on_a).to contain_exactly( + check_constraint_a_positive, + check_constraint_a_gt_b, + unique_constraint_a, + invalid_constraint_a + ) end end diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb index e41c7d34378..350e36ff1f1 100644 --- a/spec/lib/gitlab/database/tables_truncate_spec.rb +++ b/spec/lib/gitlab/database/tables_truncate_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_base, - :suppress_gitlab_schemas_validate_connection, feature_category: :cell do + :suppress_gitlab_schemas_validate_connection, feature_category: :cell do include MigrationsHelpers let(:min_batch_size) { 1 } diff --git a/spec/migrations/20231016001000_fix_design_user_mentions_design_id_note_id_index_for_self_managed_spec.rb b/spec/migrations/20231016001000_fix_design_user_mentions_design_id_note_id_index_for_self_managed_spec.rb new file mode 100644 index 00000000000..8d890bb7cac --- /dev/null +++ b/spec/migrations/20231016001000_fix_design_user_mentions_design_id_note_id_index_for_self_managed_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_migration! + +RSpec.describe FixDesignUserMentionsDesignIdNoteIdIndexForSelfManaged, feature_category: :database do + let(:connection) { described_class.new.connection } + let(:design_user_mentions) { table(:design_user_mentions) } + + shared_examples 'index `design_user_mentions_on_design_id_and_note_id_unique_index` already exists' do + it 'does not swap the columns' do + disable_migrations_output do + reversible_migration do |migration| + migration.before -> { + index = connection.indexes(:design_user_mentions).find do |i| + i.name == 'design_user_mentions_on_design_id_and_note_id_unique_index' + end + expect(index.columns).to eq(%w[design_id note_id]) + } + + migration.after -> { + index = connection.indexes(:design_user_mentions).find do |i| + i.name == 'design_user_mentions_on_design_id_and_note_id_unique_index' + end + expect(index.columns).to eq(%w[design_id note_id]) + } + end + end + end + end + + describe '#up' do + before do + # rubocop:disable RSpec/AnyInstanceOf + allow_any_instance_of(described_class).to( + receive(:com_or_dev_or_test_but_not_jh?).and_return(com_or_dev_or_test_but_not_jh?) + ) + # rubocop:enable RSpec/AnyInstanceOf + end + + context 'when GitLab.com, dev, or test' do + let(:com_or_dev_or_test_but_not_jh?) { true } + + it_behaves_like 'index `design_user_mentions_on_design_id_and_note_id_unique_index` already exists' + end + + context 'when self-managed instance' do + let(:com_or_dev_or_test_but_not_jh?) { false } + + context "when index does not exist" do + before do + connection.execute('DROP INDEX IF EXISTS design_user_mentions_on_design_id_and_note_id_unique_index') + end + + after do + connection.execute('CREATE UNIQUE INDEX IF NOT EXISTS + design_user_mentions_on_design_id_and_note_id_unique_index + ON design_user_mentions (design_id, note_id)') + end + + it 'creates the index' do + disable_migrations_output { migrate! } + + index = connection.indexes(:design_user_mentions).find do |i| + i.name == 'design_user_mentions_on_design_id_and_note_id_unique_index' + end + + expect(index.columns).to eq(%w[design_id note_id]) + end + end + + context "when index does exist" do + it_behaves_like 'index `design_user_mentions_on_design_id_and_note_id_unique_index` already exists' + end + + context "when index does exists on the int4 column" do + before do + connection.execute('DROP INDEX IF EXISTS design_user_mentions_on_design_id_and_note_id_unique_index') + connection.execute( + 'ALTER TABLE design_user_mentions ADD COLUMN IF NOT EXISTS note_id_convert_to_bigint integer' + ) + connection.execute('CREATE UNIQUE INDEX + design_user_mentions_on_design_id_and_note_id_unique_index + ON design_user_mentions (design_id, note_id_convert_to_bigint)') + end + + after do + connection.execute('DROP INDEX IF EXISTS design_user_mentions_on_design_id_and_note_id_unique_index') + connection.execute('ALTER TABLE design_user_mentions DROP COLUMN IF EXISTS note_id_convert_to_bigint') + connection.execute('CREATE UNIQUE INDEX + design_user_mentions_on_design_id_and_note_id_unique_index + ON design_user_mentions (design_id, note_id)') + end + + it 'creates the index on the int8 column' do + index = connection.indexes(:design_user_mentions).find do |i| + i.name == 'design_user_mentions_on_design_id_and_note_id_unique_index' + end + + expect(index.columns).to eq(%w[design_id note_id_convert_to_bigint]) + + disable_migrations_output { migrate! } + + index = connection.indexes(:design_user_mentions).find do |i| + i.name == 'design_user_mentions_on_design_id_and_note_id_unique_index' + end + + expect(index.columns).to eq(%w[design_id note_id]) + end + end + end + end +end diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb index 755f1ea6078..aacb696a88e 100644 --- a/spec/presenters/clusters/cluster_presenter_spec.rb +++ b/spec/presenters/clusters/cluster_presenter_spec.rb @@ -123,7 +123,7 @@ RSpec.describe Clusters::ClusterPresenter do 'clusters-path': clusterable_presenter.index_path, 'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster), 'documentation-path': help_page_path('user/infrastructure/clusters/manage/clusters_health'), - 'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'), + 'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index', anchor: 'add-a-new-dashboard-to-your-project'), 'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'), 'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'), 'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'), diff --git a/spec/rubocop/cop/gitlab/doc_url_spec.rb b/spec/rubocop/cop/gitlab/doc_url_spec.rb index 957edc8286b..fa055f9b2fe 100644 --- a/spec/rubocop/cop/gitlab/doc_url_spec.rb +++ b/spec/rubocop/cop/gitlab/doc_url_spec.rb @@ -9,7 +9,7 @@ RSpec.describe RuboCop::Cop::Gitlab::DocUrl, feature_category: :shared do it 'registers an offense' do expect_offense(<<~RUBY) 'See [the docs](https://docs.gitlab.com/ee/user/permissions#roles).' - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See https://docs.gitlab.com/ee/development/documentation/#linking-to-help-in-ruby. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See [...] RUBY end end @@ -19,7 +19,7 @@ RSpec.describe RuboCop::Cop::Gitlab::DocUrl, feature_category: :shared do expect_offense(<<~'RUBY') 'See the docs: ' \ 'https://docs.gitlab.com/ee/user/permissions#roles' - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See https://docs.gitlab.com/ee/development/documentation/#linking-to-help-in-ruby. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See [...] RUBY end end @@ -30,7 +30,7 @@ RSpec.describe RuboCop::Cop::Gitlab::DocUrl, feature_category: :shared do <<-HEREDOC See the docs: https://docs.gitlab.com/ee/user/permissions#roles - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See https://docs.gitlab.com/ee/development/documentation/#linking-to-help-in-ruby. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `#help_page_url` instead of directly including link. See [...] HEREDOC RUBY end diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index a8fd96a03bb..1faf4c6fe4c 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -149,7 +149,7 @@ RSpec.describe IssueEntity do end it 'returns archived project doc' do - expect(subject[:archived_project_docs_path]).to eq('/help/user/project/settings/index.md#archive-a-project') + expect(subject[:archived_project_docs_path]).to eq('/help/user/project/settings/index#archive-a-project') end end end diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb index 8c577749107..94db1c55a90 100644 --- a/spec/support/shared_examples/features/work_items_shared_examples.rb +++ b/spec/support/shared_examples/features/work_items_shared_examples.rb @@ -371,12 +371,12 @@ RSpec.shared_examples 'work items milestone' do it 'searches and sets or removes milestone for the work item' do click_button s_('WorkItem|Add to milestone') send_keys "\"#{milestone.title}\"" - click_button milestone.title + select_listbox_item(milestone.title, exact_text: true) expect(page).to have_button(milestone.title) click_button milestone.title - click_button s_('WorkItem|No milestone') + select_listbox_item(s_('WorkItem|No milestone'), exact_text: true) expect(page).to have_button(s_('WorkItem|Add to milestone')) end -- cgit v1.2.3