diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 21:12:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-24 21:12:18 +0300 |
commit | 06c57a837802f789b9276e23d7f505d95270f033 (patch) | |
tree | b45a80632d84a459b11376e1b575928c911eb045 /spec/frontend | |
parent | 53ab147992c8e791582f625c80811fdda5ba4d5a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
10 files changed, 317 insertions, 52 deletions
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); }); }); |