diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-07 18:19:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-07 18:19:19 +0300 |
commit | d4fcd1794ea9fc10d83cdc75490f76a418e59d52 (patch) | |
tree | b072bfe2c59dc666ddaa28c11e0c04a7971014e0 /spec/frontend/vue_shared/components | |
parent | dfa6eac07553d5a3f254ee904e4298bd666b410f (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/vue_shared/components')
3 files changed, 112 insertions, 94 deletions
diff --git a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js index 40232eb367a..810269257b6 100644 --- a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js @@ -1,15 +1,16 @@ import { + GlDisclosureDropdown, + GlDisclosureDropdownItem, GlSprintf, - GlDropdown, - GlDropdownItem, - GlDropdownText, GlSearchBoxByType, + GlIcon, } from '@gitlab/ui'; import fuzzaldrinPlus from 'fuzzaldrin-plus'; import { nextTick } from 'vue'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import { stubComponent } from 'helpers/stub_component'; import DiffStatsDropdown, { i18n } from '~/vue_shared/components/diff_stats_dropdown.vue'; +import { ARROW_DOWN_KEY } from '~/lib/utils/keys'; jest.mock('fuzzaldrin-plus', () => ({ filter: jest.fn().mockReturnValue([]), @@ -42,7 +43,7 @@ describe('Diff Stats Dropdown', () => { const focusInputMock = jest.fn(); const createComponent = ({ changed = 0, added = 0, deleted = 0, files = [] } = {}) => { - wrapper = shallowMountExtended(DiffStatsDropdown, { + wrapper = mountExtended(DiffStatsDropdown, { propsData: { changed, added, @@ -51,7 +52,6 @@ describe('Diff Stats Dropdown', () => { }, stubs: { GlSprintf, - GlDropdown, GlSearchBoxByType: stubComponent(GlSearchBoxByType, { methods: { focusInput: focusInputMock }, }), @@ -59,9 +59,8 @@ describe('Diff Stats Dropdown', () => { }); }; - const findChanged = () => wrapper.findComponent(GlDropdown); - const findChangedFiles = () => findChanged().findAllComponents(GlDropdownItem); - const findNoFilesText = () => findChanged().findComponent(GlDropdownText); + const findChanged = () => wrapper.findComponent(GlDisclosureDropdown); + const findChangedFiles = () => findChanged().findAllComponents(GlDisclosureDropdownItem); const findCollapsed = () => wrapper.findByTestId('diff-stats-additions-deletions-expanded'); const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); @@ -79,15 +78,14 @@ describe('Diff Stats Dropdown', () => { const fileText = findChangedFiles().at(1).text(); expect(fileText).toContain(mockFiles[1].name); expect(fileText).toContain(mockFiles[1].path); - expect(fileData.props()).toMatchObject({ - iconName: mockFiles[1].icon, - iconColor: mockFiles[1].iconColor, - }); + expect(fileData.findComponent(GlIcon).props('name')).toEqual(mockFiles[1].icon); + expect(fileData.findComponent(GlIcon).classes()).toContain('gl-text-red-500'); + expect(fileData.find('a').attributes('href')).toEqual(mockFiles[1].href); }); it('when no files changed', () => { createComponent({ files: [] }); - expect(findNoFilesText().text()).toContain(i18n.noFilesFound); + expect(findChanged().text()).toContain(i18n.noFilesFound); }); }); @@ -108,7 +106,7 @@ describe('Diff Stats Dropdown', () => { }); it(`dropdown header should be '${expectedDropdownHeader}'`, () => { - expect(findChanged().props('text')).toBe(expectedDropdownHeader); + expect(findChanged().props('toggleText')).toBe(expectedDropdownHeader); }); it(`added and deleted count in collapsed section should be '${expectedAddedDeletedCollapsed}'`, () => { @@ -137,27 +135,27 @@ describe('Diff Stats Dropdown', () => { }); }); - describe('selecting file dropdown item', () => { + describe('on dropdown open', () => { beforeEach(() => { - createComponent({ files: mockFiles }); + createComponent(); }); - it('updates the URL', () => { - findChangedFiles().at(0).vm.$emit('click'); - expect(window.location.hash).toBe(mockFiles[0].href); - findChangedFiles().at(1).vm.$emit('click'); - expect(window.location.hash).toBe(mockFiles[1].href); + it('should set the search input focus', () => { + findChanged().vm.$emit('shown'); + expect(focusInputMock).toHaveBeenCalled(); }); }); - describe('on dropdown open', () => { + describe('keyboard nav', () => { beforeEach(() => { - createComponent(); + createComponent({ files: mockFiles }); }); - it('should set the search input focus', () => { - findChanged().vm.$emit('shown'); - expect(focusInputMock).toHaveBeenCalled(); + it('focuses the first item when pressing the down key within the search box', () => { + const spy = jest.spyOn(wrapper.vm, 'focusFirstItem'); + findSearchBox().vm.$emit('keydown', new KeyboardEvent({ key: ARROW_DOWN_KEY })); + + expect(spy).toHaveBeenCalled(); }); }); }); diff --git a/spec/frontend/vue_shared/components/list_selector/index_spec.js b/spec/frontend/vue_shared/components/list_selector/index_spec.js index 4222a28afe8..11e64a91eb0 100644 --- a/spec/frontend/vue_shared/components/list_selector/index_spec.js +++ b/spec/frontend/vue_shared/components/list_selector/index_spec.js @@ -1,16 +1,18 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { GlCard, GlIcon, GlCollapsibleListbox, GlSearchBoxByType } from '@gitlab/ui'; +import Api from '~/api'; +import { createAlert } from '~/alert'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import ListSelector from '~/vue_shared/components/list_selector/index.vue'; import UserItem from '~/vue_shared/components/list_selector/user_item.vue'; import GroupItem from '~/vue_shared/components/list_selector/group_item.vue'; -import usersAutocompleteQuery from '~/graphql_shared/queries/users_autocomplete.query.graphql'; import groupsAutocompleteQuery from '~/graphql_shared/queries/groups_autocomplete.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { USERS_RESPONSE_MOCK, GROUPS_RESPONSE_MOCK } from './mock_data'; +jest.mock('~/alert'); Vue.use(VueApollo); describe('List Selector spec', () => { @@ -20,6 +22,7 @@ describe('List Selector spec', () => { const USERS_MOCK_PROPS = { title: 'Users', projectPath: 'some/project/path', + groupPath: 'some/group/path', type: 'users', }; @@ -29,15 +32,10 @@ describe('List Selector spec', () => { type: 'groups', }; - const usersAutocompleteQuerySuccess = jest.fn().mockResolvedValue(USERS_RESPONSE_MOCK); const groupsAutocompleteQuerySuccess = jest.fn().mockResolvedValue(GROUPS_RESPONSE_MOCK); - const createComponent = async ( - props, - query = usersAutocompleteQuery, - queryResponse = usersAutocompleteQuerySuccess, - ) => { - fakeApollo = createMockApollo([[query, queryResponse]]); + const createComponent = async (props) => { + fakeApollo = createMockApollo([[groupsAutocompleteQuery, groupsAutocompleteQuerySuccess]]); wrapper = mountExtended(ListSelector, { apolloProvider: fakeApollo, @@ -52,12 +50,21 @@ describe('List Selector spec', () => { const findCard = () => wrapper.findComponent(GlCard); const findTitle = () => findCard().find('[data-testid="list-selector-title"]'); const findIcon = () => wrapper.findComponent(GlIcon); - const findListBox = () => wrapper.findComponent(GlCollapsibleListbox); + const findAllListBoxComponents = () => wrapper.findAllComponents(GlCollapsibleListbox); + const findSearchResultsDropdown = () => findAllListBoxComponents().at(0); + const findNamespaceDropdown = () => findAllListBoxComponents().at(1); const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); const findAllUserComponents = () => wrapper.findAllComponents(UserItem); const findAllGroupComponents = () => wrapper.findAllComponents(GroupItem); + beforeEach(() => { + jest.spyOn(Api, 'projectUsers').mockResolvedValue(USERS_RESPONSE_MOCK); + jest.spyOn(Api, 'groupMembers').mockResolvedValue({ data: USERS_RESPONSE_MOCK }); + }); + describe('Users type', () => { + const search = 'foo'; + beforeEach(() => createComponent(USERS_MOCK_PROPS)); it('renders a Card component', () => { @@ -77,47 +84,67 @@ describe('List Selector spec', () => { expect(findSearchBox().exists()).toBe(true); }); + it('renders two namespace dropdown items', () => { + expect(findNamespaceDropdown().props('items').length).toBe(2); + }); + it('does not call query when search box has not received an input', () => { - expect(usersAutocompleteQuerySuccess).not.toHaveBeenCalled(); + expect(Api.projectUsers).not.toHaveBeenCalled(); + expect(Api.groupMembers).not.toHaveBeenCalled(); expect(findAllUserComponents().length).toBe(0); }); - describe('searching', () => { - const searchResponse = USERS_RESPONSE_MOCK.data.project.autocompleteUsers; - const search = 'foo'; + describe.each` + dropdownItemValue | apiMethod | apiParams | searchResponse + ${'false'} | ${'groupMembers'} | ${[USERS_MOCK_PROPS.groupPath, { query: search }]} | ${USERS_RESPONSE_MOCK} + ${'true'} | ${'projectUsers'} | ${[USERS_MOCK_PROPS.projectPath, search]} | ${USERS_RESPONSE_MOCK} + `( + 'searching based on namespace dropdown selection', + ({ dropdownItemValue, apiMethod, apiParams, searchResponse }) => { + const emitSearchInput = async () => { + findSearchBox().vm.$emit('input', search); + await waitForPromises(); + }; + + beforeEach(async () => { + findNamespaceDropdown().vm.$emit('select', dropdownItemValue); + await emitSearchInput(); + }); - const emitSearchInput = async () => { - findSearchBox().vm.$emit('input', search); - await waitForPromises(); - }; + it('shows error alert when API fails', async () => { + jest.spyOn(Api, apiMethod).mockRejectedValueOnce(); + await emitSearchInput(); - beforeEach(() => emitSearchInput()); + expect(createAlert).toHaveBeenCalledWith({ + message: 'An error occurred while fetching. Please try again.', + }); + }); - it('calls query with correct variables when Search box receives an input', () => { - expect(usersAutocompleteQuerySuccess).toHaveBeenCalledWith({ - fullPath: USERS_MOCK_PROPS.projectPath, - isProject: true, - search, + it('calls query with correct variables when Search box receives an input', () => { + expect(Api[apiMethod]).toHaveBeenCalledWith(...apiParams); }); - }); - it('renders a List box component with the correct props', () => { - expect(findListBox().props()).toMatchObject({ multiple: true, items: searchResponse }); - }); + it('renders a List box component with the correct props', () => { + expect(findSearchResultsDropdown().props()).toMatchObject({ + multiple: true, + items: searchResponse, + }); + }); - it('renders a user component for each search result', () => { - expect(findAllUserComponents().length).toBe(searchResponse.length); - }); + it('renders a user component for each search result', () => { + expect(findAllUserComponents().length).toBe(searchResponse.length); + }); - it('emits an event when a search result is selected', () => { - const firstSearchResult = searchResponse[0]; - findAllUserComponents().at(0).vm.$emit('select', firstSearchResult.username); + it('emits an event when a search result is selected', () => { + const firstSearchResult = searchResponse[0]; + findAllUserComponents().at(0).vm.$emit('select', firstSearchResult.username); - expect(wrapper.emitted('select')).toEqual([ - [{ ...firstSearchResult, text: 'Administrator', value: 'root' }], - ]); - }); - }); + expect(wrapper.emitted('select')).toEqual([ + [{ ...firstSearchResult, text: 'Administrator', value: 'root' }], + ]); + }); + }, + ); describe('selected items', () => { const selectedUser = { username: 'root' }; @@ -147,9 +174,7 @@ describe('List Selector spec', () => { }); describe('Groups type', () => { - beforeEach(() => - createComponent(GROUPS_MOCK_PROPS, groupsAutocompleteQuery, groupsAutocompleteQuerySuccess), - ); + beforeEach(() => createComponent(GROUPS_MOCK_PROPS)); it('renders a correct title', () => { expect(findTitle().exists()).toBe(true); @@ -182,8 +207,11 @@ describe('List Selector spec', () => { }); }); - it('renders a List box component with the correct props', () => { - expect(findListBox().props()).toMatchObject({ multiple: true, items: searchResponse }); + it('renders a dropdown for the search results', () => { + expect(findSearchResultsDropdown().props()).toMatchObject({ + multiple: true, + items: searchResponse, + }); }); it('renders a group component for each search result', () => { diff --git a/spec/frontend/vue_shared/components/list_selector/mock_data.js b/spec/frontend/vue_shared/components/list_selector/mock_data.js index 25ecac9632b..5b44a0c2a83 100644 --- a/spec/frontend/vue_shared/components/list_selector/mock_data.js +++ b/spec/frontend/vue_shared/components/list_selector/mock_data.js @@ -1,28 +1,20 @@ -export const USERS_RESPONSE_MOCK = { - data: { - project: { - id: 'gid://gitlab/Project/20', - autocompleteUsers: [ - { - id: 'gid://gitlab/User/1', - avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png', - name: 'Administrator', - username: 'root', - __typename: 'AutocompletedUser', - }, - { - id: 'gid://gitlab/User/15', - avatarUrl: - 'https://www.gravatar.com/avatar/c4ab964b90c3049c47882b319d3c5cc0?s=80\u0026d=identicon', - name: 'Corrine Rath', - username: 'laronda.graham', - __typename: 'AutocompletedUser', - }, - ], - __typename: 'Project', - }, +export const USERS_RESPONSE_MOCK = [ + { + id: 'gid://gitlab/User/1', + avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png', + name: 'Administrator', + username: 'root', + __typename: 'AutocompletedUser', }, -}; + { + id: 'gid://gitlab/User/15', + avatarUrl: + 'https://www.gravatar.com/avatar/c4ab964b90c3049c47882b319d3c5cc0?s=80\u0026d=identicon', + name: 'Corrine Rath', + username: 'laronda.graham', + __typename: 'AutocompletedUser', + }, +]; export const GROUPS_RESPONSE_MOCK = { data: { |