diff options
Diffstat (limited to 'spec/frontend')
5 files changed, 183 insertions, 328 deletions
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); }; |