diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 18:12:18 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 18:12:18 +0000 |
| commit | 06c57a837802f789b9276e23d7f505d95270f033 (patch) | |
| tree | b45a80632d84a459b11376e1b575928c911eb045 /spec | |
| parent | 53ab147992c8e791582f625c80811fdda5ba4d5a (diff) | |
| download | gitlab-ce-06c57a837802f789b9276e23d7f505d95270f033.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
17 files changed, 437 insertions, 88 deletions
diff --git a/spec/features/help_dropdown_spec.rb b/spec/features/help_dropdown_spec.rb index db98f58240d..e64c19d4708 100644 --- a/spec/features/help_dropdown_spec.rb +++ b/spec/features/help_dropdown_spec.rb @@ -51,7 +51,8 @@ RSpec.describe "Help Dropdown", :js do visit root_path end - it 'renders correct version badge variant' do + it 'renders correct version badge variant', + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369850' do page.within '.header-help' do find('.header-help-dropdown-toggle').click diff --git a/spec/frontend/blob/blob_blame_link_spec.js b/spec/frontend/blob/blob_blame_link_spec.js new file mode 100644 index 00000000000..0d19177a11f --- /dev/null +++ b/spec/frontend/blob/blob_blame_link_spec.js @@ -0,0 +1,47 @@ +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; +import addBlameLink from '~/blob/blob_blame_link'; + +describe('Blob links', () => { + const mouseoverEvent = new MouseEvent('mouseover', { + view: window, + bubbles: true, + cancelable: true, + }); + + beforeEach(() => { + setHTMLFixture(` + <div id="blob-content-holder"> + <div class="line-numbers" data-blame-path="/blamePath"> + <a id="L5" href="#L5" data-line-number="5" class="file-line-num js-line-links">5</a> + </div> + <pre id="LC5">Line 5 content</pre> + </div> + `); + + addBlameLink('#blob-content-holder', 'js-line-links'); + document.querySelector('.file-line-num').dispatchEvent(mouseoverEvent); + }); + + afterEach(() => { + resetHTMLFixture(); + }); + + it('adds wrapper elements with correct classes', () => { + const wrapper = document.querySelector('.line-links'); + + expect(wrapper).toBeTruthy(); + expect(wrapper.classList).toContain('diff-line-num'); + }); + + it('adds blame link with correct classes and path', () => { + const blameLink = document.querySelector('.file-line-blame'); + expect(blameLink).toBeTruthy(); + expect(blameLink.getAttribute('href')).toBe('/blamePath#L5'); + }); + + it('adds line link within wraper with correct classes and path', () => { + const lineLink = document.querySelector('.file-line-num'); + expect(lineLink).toBeTruthy(); + expect(lineLink.getAttribute('href')).toBe('#L5'); + }); +}); diff --git a/spec/frontend/blob/blob_links_tracking_spec.js b/spec/frontend/blob/blob_links_tracking_spec.js index 22e087bc180..8ef1e9f0ac9 100644 --- a/spec/frontend/blob/blob_links_tracking_spec.js +++ b/spec/frontend/blob/blob_links_tracking_spec.js @@ -15,7 +15,7 @@ describe('Blob links Tracking', () => { beforeEach(() => { setHTMLFixture(` - <div id="blob-content-holder"> + <div class="file-holder"> <div class="line-links diff-line-num"> <a href="#L5" class="file-line-blame"></a> <a id="L5" href="#L5" data-line-number="5" class="file-line-num">5</a> @@ -23,7 +23,7 @@ describe('Blob links Tracking', () => { <pre id="LC5">Line 5 content</pre> </div> `); - addBlobLinksTracking('#blob-content-holder', eventsToTrack); + addBlobLinksTracking(); jest.spyOn(Tracking, 'event'); }); diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js index f6c66baa6aa..01bad53d9e1 100644 --- a/spec/frontend/boards/components/board_card_move_to_position_spec.js +++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js @@ -4,8 +4,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue'; -import { createStore } from '~/boards/stores'; -import { mockList, mockIssue2 } from 'jest/boards/mock_data'; +import { mockList, mockIssue2, mockIssue, mockIssue3, mockIssue4 } from 'jest/boards/mock_data'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; Vue.use(Vuex); @@ -21,7 +20,26 @@ describe('Board Card Move to position', () => { let store; let dispatch; - store = new Vuex.Store(); + const createStoreOptions = () => { + const state = { + pageInfoByListId: { + 'gid://gitlab/List/1': {}, + 'gid://gitlab/List/2': { hasNextPage: true }, + }, + }; + const getters = { + getBoardItemsByList: () => () => [mockIssue, mockIssue2, mockIssue3, mockIssue4], + }; + const actions = { + moveItem: jest.fn(), + }; + + return { + state, + getters, + actions, + }; + }; const createComponent = (propsData) => { wrapper = shallowMountExtended(BoardCardMoveToPosition, { @@ -40,7 +58,7 @@ describe('Board Card Move to position', () => { }; beforeEach(() => { - store = createStore(); + store = new Vuex.Store(createStoreOptions()); createComponent(); }); @@ -62,13 +80,26 @@ describe('Board Card Move to position', () => { it('is opened on the click of vertical ellipsis and has 2 dropdown items when number of list items < 10', () => { findMoveToPositionDropdown().vm.$emit('click'); - expect(findDropdownItems()).toHaveLength(dropdownOptions.length); }); + + it('is opened on the click of vertical ellipsis and has 1 dropdown items when number of list items > 10', () => { + wrapper.destroy(); + + createComponent({ + list: { + ...mockList, + id: 'gid://gitlab/List/2', + }, + }); + findMoveToPositionDropdown().vm.$emit('click'); + expect(findDropdownItems()).toHaveLength(1); + }); }); describe('Dropdown options', () => { beforeEach(() => { + createComponent({ index: 1 }); trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {}); }); @@ -78,12 +109,12 @@ describe('Board Card Move to position', () => { }); it.each` - dropdownIndex | dropdownLabel | startActionCalledTimes | trackLabel - ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${0} | ${'move_to_start'} - ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${1} | ${'move_to_end'} + dropdownIndex | dropdownLabel | trackLabel | moveAfterId | moveBeforeId + ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${mockIssue.id} | ${undefined} + ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${undefined} | ${mockIssue4.id} `( 'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel', - async ({ dropdownIndex, startActionCalledTimes, dropdownLabel, trackLabel }) => { + async ({ dropdownIndex, dropdownLabel, trackLabel, moveAfterId, moveBeforeId }) => { await findEllipsesButton().vm.$emit('click'); expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel); @@ -98,18 +129,15 @@ describe('Board Card Move to position', () => { label: trackLabel, property: 'type_card', }); - expect(dispatch).toHaveBeenCalledTimes(startActionCalledTimes); - if (startActionCalledTimes) { - expect(dispatch).toHaveBeenCalledWith('moveItem', { - fromListId: mockList.id, - itemId: mockIssue2.id, - itemIid: mockIssue2.iid, - itemPath: mockIssue2.referencePath, - moveBeforeId: undefined, - moveAfterId: undefined, - toListId: mockList.id, - }); - } + expect(dispatch).toHaveBeenCalledWith('moveItem', { + fromListId: mockList.id, + itemId: mockIssue2.id, + itemIid: mockIssue2.iid, + itemPath: mockIssue2.referencePath, + moveBeforeId, + moveAfterId, + toListId: mockList.id, + }); }, ); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js new file mode 100644 index 00000000000..0700cf5d529 --- /dev/null +++ b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js @@ -0,0 +1,126 @@ +import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'; +import { nextTick } from 'vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue'; +import { createTestEditor } from '../../test_utils'; + +jest.mock('@tiptap/extension-bubble-menu'); + +describe('content_editor/components/bubble_menus/bubble_menu', () => { + let wrapper; + let tiptapEditor; + const pluginKey = 'key'; + const shouldShow = jest.fn(); + const tippyOptions = { placement: 'bottom' }; + const pluginInitializationResult = {}; + + const buildEditor = () => { + tiptapEditor = createTestEditor(); + }; + + const createWrapper = (propsData = {}) => { + wrapper = shallowMountExtended(BubbleMenu, { + provide: { + tiptapEditor, + }, + propsData: { + pluginKey, + shouldShow, + tippyOptions, + ...propsData, + }, + slots: { + default: '<div>menu content</div>', + }, + }); + }; + + const setupMocks = () => { + BubbleMenuPlugin.mockReturnValueOnce(pluginInitializationResult); + jest.spyOn(tiptapEditor, 'registerPlugin').mockImplementationOnce(() => true); + }; + + const invokeTippyEvent = (eventName, eventArgs) => { + const pluginConfig = BubbleMenuPlugin.mock.calls[0][0]; + + pluginConfig.tippyOptions[eventName](eventArgs); + }; + + beforeEach(() => { + buildEditor(); + setupMocks(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('initializes BubbleMenuPlugin', async () => { + createWrapper({}); + + await nextTick(); + + expect(BubbleMenuPlugin).toHaveBeenCalledWith({ + pluginKey, + editor: tiptapEditor, + shouldShow, + element: wrapper.vm.$el, + tippyOptions: expect.objectContaining({ + onHidden: expect.any(Function), + onShow: expect.any(Function), + ...tippyOptions, + }), + }); + + expect(tiptapEditor.registerPlugin).toHaveBeenCalledWith(pluginInitializationResult); + }); + + it('does not render default slot by default', async () => { + createWrapper({}); + + await nextTick(); + + expect(wrapper.text()).not.toContain('menu content'); + }); + + describe('when onShow event handler is invoked', () => { + const onShowArgs = {}; + + beforeEach(async () => { + createWrapper({}); + + await nextTick(); + + invokeTippyEvent('onShow', onShowArgs); + }); + + it('displays the menu content', () => { + expect(wrapper.text()).toContain('menu content'); + }); + + it('emits show event', () => { + expect(wrapper.emitted('show')).toEqual([[onShowArgs]]); + }); + }); + + describe('when onHidden event handler is invoked', () => { + const onHiddenArgs = {}; + + beforeEach(async () => { + createWrapper({}); + + await nextTick(); + + invokeTippyEvent('onShow', onHiddenArgs); + invokeTippyEvent('onHidden', onHiddenArgs); + }); + + it('displays the menu content', () => { + expect(wrapper.text()).not.toContain('menu content'); + }); + + it('emits show event', () => { + expect(wrapper.emitted('hidden')).toEqual([[onHiddenArgs]]); + }); + }); +}); diff --git a/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js index f2c0ec59ccd..378b11f4ae9 100644 --- a/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js @@ -1,4 +1,3 @@ -import { BubbleMenu } from '@tiptap/vue-2'; import { GlDropdown, GlDropdownForm, @@ -11,6 +10,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper'; import { stubComponent } from 'helpers/stub_component'; import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block_bubble_menu.vue'; import eventHubFactory from '~/helpers/event_hub_factory'; +import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue'; import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight'; import Diagram from '~/content_editor/extensions/diagram'; import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader'; @@ -40,6 +40,7 @@ describe('content_editor/components/bubble_menus/code_block_bubble_menu', () => }, stubs: { GlDropdownItem: stubComponent(GlDropdownItem), + BubbleMenu: stubComponent(BubbleMenu), }, }); }; @@ -73,7 +74,6 @@ describe('content_editor/components/bubble_menus/code_block_bubble_menu', () => await emitEditorEvent({ event: 'transaction', tiptapEditor }); - expect(bubbleMenu.props('editor')).toBe(tiptapEditor); expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js index 38ea9dc2b21..cce17176129 100644 --- a/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js @@ -1,7 +1,8 @@ -import { BubbleMenu } from '@tiptap/vue-2'; import { mockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting_bubble_menu.vue'; +import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue'; +import { stubComponent } from 'helpers/stub_component'; import { BUBBLE_MENU_TRACKING_ACTION, @@ -25,6 +26,9 @@ describe('content_editor/components/bubble_menus/formatting_bubble_menu', () => provide: { tiptapEditor, }, + stubs: { + BubbleMenu: stubComponent(BubbleMenu), + }, }); }; @@ -41,7 +45,6 @@ describe('content_editor/components/bubble_menus/formatting_bubble_menu', () => buildWrapper(); const bubbleMenu = wrapper.findComponent(BubbleMenu); - expect(bubbleMenu.props().editor).toBe(tiptapEditor); expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']); }); diff --git a/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js index 04bf4efb7e2..7f8c770e503 100644 --- a/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js @@ -1,8 +1,9 @@ import { GlLink, GlForm } from '@gitlab/ui'; -import { BubbleMenu } from '@tiptap/vue-2'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue'; import eventHubFactory from '~/helpers/event_hub_factory'; +import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue'; +import { stubComponent } from 'helpers/stub_component'; import Link from '~/content_editor/extensions/link'; import { createTestEditor, emitEditorEvent } from '../../test_utils'; @@ -28,6 +29,9 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => { contentEditor, eventHub, }, + stubs: { + BubbleMenu: stubComponent(BubbleMenu), + }, }); }; @@ -60,7 +64,6 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => { }); it('renders bubble menu component', async () => { - expect(bubbleMenu.props('editor')).toBe(tiptapEditor); expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']); }); @@ -157,19 +160,6 @@ describe('content_editor/components/bubble_menus/link_bubble_menu', () => { expect(to).toBe(18); }); - it('shows the copy/edit/remove link buttons again if selection changes to another non-link and then back again to a link', async () => { - expectLinkButtonsToExist(false); - - tiptapEditor.commands.setTextSelection(3); - await emitEditorEvent({ event: 'transaction', tiptapEditor }); - - tiptapEditor.commands.setTextSelection(14); - await emitEditorEvent({ event: 'transaction', tiptapEditor }); - - expectLinkButtonsToExist(true); - expect(wrapper.findComponent(GlForm).exists()).toBe(false); - }); - describe('after making changes in the form and clicking apply', () => { beforeEach(async () => { linkHrefInput.setValue('https://google.com'); diff --git a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js index 51f46389526..c34862b6f3f 100644 --- a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js +++ b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js @@ -1,7 +1,8 @@ import { GlLink, GlForm } from '@gitlab/ui'; -import { BubbleMenu } from '@tiptap/vue-2'; import { mountExtended } from 'helpers/vue_test_utils_helper'; +import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue'; import MediaBubbleMenu from '~/content_editor/components/bubble_menus/media_bubble_menu.vue'; +import { stubComponent } from 'helpers/stub_component'; import eventHubFactory from '~/helpers/event_hub_factory'; import Image from '~/content_editor/extensions/image'; import Audio from '~/content_editor/extensions/audio'; @@ -54,6 +55,9 @@ describe.each` contentEditor, eventHub, }, + stubs: { + BubbleMenu: stubComponent(BubbleMenu), + }, }); }; @@ -94,7 +98,6 @@ describe.each` }); it('renders bubble menu component', async () => { - expect(bubbleMenu.props('editor')).toBe(tiptapEditor); expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']); }); diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js index 534c0baf35d..05c259de370 100644 --- a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js @@ -110,7 +110,7 @@ describe('Merge request widget rebase component', () => { expect(findRebaseMessageText()).toContain('Something went wrong!'); }); - describe('Rebase buttons with', () => { + describe('Rebase buttons', () => { beforeEach(() => { createWrapper( { @@ -148,6 +148,79 @@ describe('Merge request widget rebase component', () => { expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true }); }); }); + + describe('Rebase when pipelines must succeed is enabled', () => { + beforeEach(() => { + createWrapper( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + onlyAllowMergeIfPipelineSucceeds: true, + }, + service: { + rebase: rebaseMock, + poll: pollMock, + }, + }, + mergeRequestWidgetGraphql, + ); + }); + + it('renders only the rebase button', () => { + expect(findRebaseWithoutCiButton().exists()).toBe(false); + expect(findStandardRebaseButton().exists()).toBe(true); + }); + + it('starts the rebase when clicking', async () => { + findStandardRebaseButton().vm.$emit('click'); + + await nextTick(); + + expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false }); + }); + }); + + describe('Rebase when pipelines must succeed and skipped pipelines are considered successful are enabled', () => { + beforeEach(() => { + createWrapper( + { + mr: { + rebaseInProgress: false, + canPushToSourceBranch: true, + onlyAllowMergeIfPipelineSucceeds: true, + allowMergeOnSkippedPipeline: true, + }, + service: { + rebase: rebaseMock, + poll: pollMock, + }, + }, + mergeRequestWidgetGraphql, + ); + }); + + it('renders both rebase buttons', () => { + expect(findRebaseWithoutCiButton().exists()).toBe(true); + expect(findStandardRebaseButton().exists()).toBe(true); + }); + + it('starts the rebase when clicking', async () => { + findStandardRebaseButton().vm.$emit('click'); + + await nextTick(); + + expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false }); + }); + + it('starts the CI-skipping rebase when clicking on "Rebase without CI"', async () => { + findRebaseWithoutCiButton().vm.$emit('click'); + + await nextTick(); + + expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true }); + }); + }); }); describe('without permissions', () => { diff --git a/spec/frontend/work_items/components/work_item_information_spec.js b/spec/frontend/work_items/components/work_item_information_spec.js index d5f6921c2bc..887c5f615e9 100644 --- a/spec/frontend/work_items/components/work_item_information_spec.js +++ b/spec/frontend/work_items/components/work_item_information_spec.js @@ -8,7 +8,6 @@ const createComponent = () => mount(WorkItemInformation); describe('Work item information alert', () => { let wrapper; const tasksHelpPath = helpPagePath('user/tasks'); - const workItemsHelpPath = helpPagePath('development/work_items'); const findAlert = () => wrapper.findComponent(GlAlert); const findHelpLink = () => wrapper.findComponent(GlLink); @@ -33,16 +32,12 @@ describe('Work item information alert', () => { expect(findAlert().props('variant')).toBe('tip'); }); - it('should have the correct text for primary button and link', () => { + it('should have the correct text for title', () => { expect(findAlert().props('title')).toBe(WorkItemInformation.i18n.tasksInformationTitle); - expect(findAlert().props('primaryButtonText')).toBe( - WorkItemInformation.i18n.learnTasksButtonText, - ); - expect(findAlert().props('primaryButtonLink')).toBe(tasksHelpPath); }); it('should have the correct link to work item link', () => { expect(findHelpLink().exists()).toBe(true); - expect(findHelpLink().attributes('href')).toBe(workItemsHelpPath); + expect(findHelpLink().attributes('href')).toBe(tasksHelpPath); }); }); diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb index ad2a3c7b462..3e8d71ac673 100644 --- a/spec/lib/gitlab/alert_management/payload/base_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb @@ -347,4 +347,26 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do it { is_expected.to be(true) } end + + describe '#source' do + subject { parsed_payload.source } + + it { is_expected.to be_nil } + + context 'with alerting integration provided' do + before do + parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION') + end + + it { is_expected.to eq('INTEGRATION') } + end + + context 'with monitoring tool defined in the raw payload' do + before do + allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL') + end + + it { is_expected.to eq('TOOL') } + end + end end diff --git a/spec/lib/gitlab/alert_management/payload/generic_spec.rb b/spec/lib/gitlab/alert_management/payload/generic_spec.rb index 59933f7459d..bc3b6edc638 100644 --- a/spec/lib/gitlab/alert_management/payload/generic_spec.rb +++ b/spec/lib/gitlab/alert_management/payload/generic_spec.rb @@ -144,4 +144,40 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do it { is_expected.to eq(value) } end end + + describe '#resolved?' do + subject { parsed_payload.resolved? } + + context 'without end time' do + it { is_expected.to eq(false) } + end + + context 'with end time' do + let(:raw_payload) { { 'end_time' => Time.current.to_s } } + + it { is_expected.to eq(true) } + end + end + + describe '#source' do + subject { parsed_payload.source } + + it { is_expected.to eq('Generic Alert Endpoint') } + + context 'with alerting integration provided' do + before do + parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION') + end + + it { is_expected.to eq('INTEGRATION') } + end + + context 'with monitoring tool defined in the raw payload' do + before do + allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL') + end + + it { is_expected.to eq('TOOL') } + end + end end diff --git a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb index 6996249cb40..45932defaf9 100644 --- a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb +++ b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb @@ -50,8 +50,8 @@ RSpec.describe Gitlab::BackgroundMigration::RenameTaskSystemNoteToChecklistItem let(:migration) do described_class.new( - start_id: note1.id, - end_id: note4.id, + start_id: metadata1.id, + end_id: metadata4.id, batch_table: :system_note_metadata, batch_column: :id, sub_batch_size: 2, diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index cc696d76a02..2c0f6943243 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -586,13 +586,36 @@ RSpec.describe API::Branches do let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/unprotect" } shared_examples_for 'repository unprotected branch' do - it 'unprotects a single branch' do - put api(route, current_user) + context 'when branch is protected' do + let!(:protected_branch) { create(:protected_branch, project: project, name: protected_branch_name) } - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('public_api/v4/branch') - expect(json_response['name']).to eq(CGI.unescape(branch_name)) - expect(json_response['protected']).to eq(false) + it 'unprotects a single branch' do + expect_next_instance_of(::ProtectedBranches::DestroyService, project, current_user) do |instance| + expect(instance).to receive(:execute).with(protected_branch).and_call_original + end + + put api(route, current_user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) + expect(json_response['protected']).to eq(false) + + expect { protected_branch.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context 'when branch is not protected' do + it 'returns a single branch response' do + expect(::ProtectedBranches::DestroyService).not_to receive(:new) + + put api(route, current_user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) + expect(json_response['protected']).to eq(false) + end end context 'when branch does not exist' do @@ -637,40 +660,40 @@ RSpec.describe API::Branches do context 'when authenticated', 'as a maintainer' do let(:current_user) { user } + let(:protected_branch_name) { branch_name } - context "when a protected branch doesn't already exist" do - it_behaves_like 'repository unprotected branch' + it_behaves_like 'repository unprotected branch' - context 'when branch contains a dot' do - let(:branch_name) { branch_with_dot } + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot } - it_behaves_like 'repository unprotected branch' - end + it_behaves_like 'repository unprotected branch' + end - context 'when branch contains a slash' do - let(:branch_name) { branch_with_slash } + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash } - it_behaves_like '404 response' do - let(:request) { put api(route, current_user) } - end + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } end + end - context 'when branch contains an escaped slash' do - let(:branch_name) { CGI.escape(branch_with_slash) } + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash) } + let(:protected_branch_name) { branch_with_slash } - it_behaves_like 'repository unprotected branch' - end + it_behaves_like 'repository unprotected branch' + end - context 'requesting with the escaped project full path' do - let(:project_id) { CGI.escape(project.full_path) } + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } - it_behaves_like 'repository unprotected branch' + it_behaves_like 'repository unprotected branch' - context 'when branch contains a dot' do - let(:branch_name) { branch_with_dot } + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot } - it_behaves_like 'repository unprotected branch' - end + it_behaves_like 'repository unprotected branch' end end end diff --git a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb index feaa8070090..25008bca619 100644 --- a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb +++ b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb @@ -73,6 +73,10 @@ RSpec.shared_examples 'graphql query for searching issuables' do resolve_issuables(search: 'created') end end + + it 'does not return error if search term is not present' do + expect(resolve_issuables).not_to be_instance_of(Gitlab::Graphql::Errors::ArgumentError) + end end context 'with disable_anonymous_search as `false`' do diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb index 9546b6cbea4..0db9519f760 100644 --- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb @@ -23,12 +23,10 @@ RSpec.shared_examples 'creates an alert management alert or errors' do end context 'and fails to save' do - let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] }, '[]': [] ) } - before do - allow(service).to receive(:alert).and_call_original - allow(service).to receive_message_chain(:alert, :save).and_return(false) - allow(service).to receive_message_chain(:alert, :errors).and_return(errors) + allow(AlertManagement::Alert).to receive(:new).and_wrap_original do |m, **args| + m.call(**args, hosts: ['a' * 256]) # hosts should be 255 + end end it_behaves_like 'alerts service responds with an error', :bad_request |
