diff options
Diffstat (limited to 'spec/frontend/vue_shared/components/group_select/group_select_spec.js')
-rw-r--r-- | spec/frontend/vue_shared/components/group_select/group_select_spec.js | 150 |
1 files changed, 135 insertions, 15 deletions
diff --git a/spec/frontend/vue_shared/components/group_select/group_select_spec.js b/spec/frontend/vue_shared/components/group_select/group_select_spec.js index c10b32c6acc..87dd7795b98 100644 --- a/spec/frontend/vue_shared/components/group_select/group_select_spec.js +++ b/spec/frontend/vue_shared/components/group_select/group_select_spec.js @@ -1,20 +1,18 @@ import { nextTick } from 'vue'; -import { GlCollapsibleListbox } from '@gitlab/ui'; +import { GlFormGroup, GlCollapsibleListbox } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import axios from '~/lib/utils/axios_utils'; -import { createAlert } from '~/flash'; import GroupSelect from '~/vue_shared/components/group_select/group_select.vue'; import { TOGGLE_TEXT, + RESET_LABEL, FETCH_GROUPS_ERROR, FETCH_GROUP_ERROR, QUERY_TOO_SHORT_MESSAGE, } from '~/vue_shared/components/group_select/constants'; import waitForPromises from 'helpers/wait_for_promises'; -jest.mock('~/flash'); - describe('GroupSelect', () => { let wrapper; let mock; @@ -26,22 +24,34 @@ describe('GroupSelect', () => { }; const groupEndpoint = `/api/undefined/groups/${groupMock.id}`; + // Stubs + const GlAlert = { + template: '<div><slot /></div>', + }; + // Props + const label = 'label'; const inputName = 'inputName'; const inputId = 'inputId'; // Finders + const findFormGroup = () => wrapper.findComponent(GlFormGroup); const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); const findInput = () => wrapper.findByTestId('input'); + const findAlert = () => wrapper.findComponent(GlAlert); // Helpers const createComponent = ({ props = {} } = {}) => { wrapper = shallowMountExtended(GroupSelect, { propsData: { + label, inputName, inputId, ...props, }, + stubs: { + GlAlert, + }, }); }; const openListbox = () => findListbox().vm.$emit('shown'); @@ -65,6 +75,12 @@ describe('GroupSelect', () => { mock.restore(); }); + it('passes the label to GlFormGroup', () => { + createComponent(); + + expect(findFormGroup().attributes('label')).toBe(label); + }); + describe('on mount', () => { it('fetches groups when the listbox is opened', async () => { createComponent(); @@ -94,13 +110,13 @@ describe('GroupSelect', () => { .reply(200, [{ full_name: 'notTheSelectedGroup', id: '2' }]); mock.onGet(groupEndpoint).reply(500); createComponent({ props: { initialSelection: groupMock.id } }); + + expect(findAlert().exists()).toBe(false); + await waitForPromises(); - expect(createAlert).toHaveBeenCalledWith({ - message: FETCH_GROUP_ERROR, - error: expect.any(Error), - parent: wrapper.vm.$el, - }); + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(FETCH_GROUP_ERROR); }); }); }); @@ -109,13 +125,12 @@ describe('GroupSelect', () => { mock.onGet('/api/undefined/groups.json').reply(500); createComponent(); openListbox(); + expect(findAlert().exists()).toBe(false); + await waitForPromises(); - expect(createAlert).toHaveBeenCalledWith({ - message: FETCH_GROUPS_ERROR, - error: expect.any(Error), - parent: wrapper.vm.$el, - }); + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(FETCH_GROUPS_ERROR); }); describe('selection', () => { @@ -186,7 +201,11 @@ describe('GroupSelect', () => { await waitForPromises(); expect(mock.history.get).toHaveLength(2); - expect(mock.history.get[1].params).toStrictEqual({ search: searchString }); + expect(mock.history.get[1].params).toStrictEqual({ + page: 1, + per_page: 20, + search: searchString, + }); }); it('shows a notice if the search query is too short', async () => { @@ -199,4 +218,105 @@ describe('GroupSelect', () => { expect(findListbox().props('noResultsText')).toBe(QUERY_TOO_SHORT_MESSAGE); }); }); + + describe('pagination', () => { + const searchString = 'searchString'; + + beforeEach(async () => { + let requestCount = 0; + mock.onGet('/api/undefined/groups.json').reply(({ params }) => { + requestCount += 1; + return [ + 200, + [ + { + full_name: `Group [page: ${params.page} - search: ${params.search}]`, + id: requestCount, + }, + ], + { + page: params.page, + 'x-total-pages': 3, + }, + ]; + }); + createComponent(); + openListbox(); + findListbox().vm.$emit('bottom-reached'); + return waitForPromises(); + }); + + it('fetches the next page when bottom is reached', async () => { + expect(mock.history.get).toHaveLength(2); + expect(mock.history.get[1].params).toStrictEqual({ + page: 2, + per_page: 20, + search: '', + }); + }); + + it('fetches the first page when the search query changes', async () => { + search(searchString); + await waitForPromises(); + + expect(mock.history.get).toHaveLength(3); + expect(mock.history.get[2].params).toStrictEqual({ + page: 1, + per_page: 20, + search: searchString, + }); + }); + + it('retains the search query when infinite scrolling', async () => { + search(searchString); + await waitForPromises(); + findListbox().vm.$emit('bottom-reached'); + await waitForPromises(); + + expect(mock.history.get).toHaveLength(4); + expect(mock.history.get[3].params).toStrictEqual({ + page: 2, + per_page: 20, + search: searchString, + }); + }); + + it('pauses infinite scroll after fetching the last page', async () => { + expect(findListbox().props('infiniteScroll')).toBe(true); + + findListbox().vm.$emit('bottom-reached'); + await waitForPromises(); + + expect(findListbox().props('infiniteScroll')).toBe(false); + }); + + it('resumes infinite scroll when search query changes', async () => { + findListbox().vm.$emit('bottom-reached'); + await waitForPromises(); + + expect(findListbox().props('infiniteScroll')).toBe(false); + + search(searchString); + await waitForPromises(); + + expect(findListbox().props('infiniteScroll')).toBe(true); + }); + }); + + it.each` + description | clearable | expectedLabel + ${'passes'} | ${true} | ${RESET_LABEL} + ${'does not pass'} | ${false} | ${''} + `( + '$description the reset button label to the listbox when clearable is $clearable', + ({ clearable, expectedLabel }) => { + createComponent({ + props: { + clearable, + }, + }); + + expect(findListbox().props('resetButtonLabel')).toBe(expectedLabel); + }, + ); }); |