diff options
Diffstat (limited to 'spec/frontend/import_entities/import_groups/components/import_table_spec.js')
-rw-r--r-- | spec/frontend/import_entities/import_groups/components/import_table_spec.js | 235 |
1 files changed, 130 insertions, 105 deletions
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js index f43e545e049..6e3df21e30a 100644 --- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js +++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js @@ -1,39 +1,30 @@ -import { - GlButton, - GlEmptyState, - GlLoadingIcon, - GlSearchBoxByClick, - GlDropdown, - GlDropdownItem, - GlTable, -} from '@gitlab/ui'; -import { mount, createLocalVue } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; +import MockAdapter from 'axios-mock-adapter'; import createMockApollo from 'helpers/mock_apollo_helper'; -import stubChildren from 'helpers/stub_children'; -import { stubComponent } from 'helpers/stub_component'; import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import httpStatus from '~/lib/utils/http_status'; +import axios from '~/lib/utils/axios_utils'; import { STATUSES } from '~/import_entities/constants'; -import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue'; +import { i18n } from '~/import_entities/import_groups/constants'; import ImportTable from '~/import_entities/import_groups/components/import_table.vue'; -import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue'; import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql'; -import setImportTargetMutation from '~/import_entities/import_groups/graphql/mutations/set_import_target.mutation.graphql'; import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; import { availableNamespacesFixture, generateFakeEntry } from '../graphql/fixtures'; -const localVue = createLocalVue(); -localVue.use(VueApollo); +jest.mock('~/flash'); +jest.mock('~/import_entities/import_groups/services/status_poller'); -const GlDropdownStub = stubComponent(GlDropdown, { - template: '<div><h1 ref="text"><slot name="button-content"></slot></h1><slot></slot></div>', -}); +Vue.use(VueApollo); describe('import table', () => { let wrapper; let apolloProvider; + let axiosMock; const SOURCE_URL = 'https://demo.host'; const FAKE_GROUP = generateFakeEntry({ id: 1, status: STATUSES.NONE }); @@ -44,76 +35,81 @@ describe('import table', () => { const FAKE_PAGE_INFO = { page: 1, perPage: 20, total: 40, totalPages: 2 }; const findImportSelectedButton = () => - wrapper.findAllComponents(GlButton).wrappers.find((w) => w.text() === 'Import selected'); - const findPaginationDropdown = () => wrapper.findComponent(GlDropdown); - const findPaginationDropdownText = () => findPaginationDropdown().find({ ref: 'text' }).text(); + wrapper.findAll('button').wrappers.find((w) => w.text() === 'Import selected'); + const findImportButtons = () => + wrapper.findAll('button').wrappers.filter((w) => w.text() === 'Import'); + const findPaginationDropdown = () => wrapper.find('[aria-label="Page size"]'); + const findPaginationDropdownText = () => findPaginationDropdown().find('button').text(); - // TODO: remove this ugly approach when - // issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531 - const findTable = () => wrapper.vm.getTableRef(); + const selectRow = (idx) => + wrapper.findAll('tbody td input[type=checkbox]').at(idx).trigger('click'); - const createComponent = ({ bulkImportSourceGroups }) => { + const createComponent = ({ bulkImportSourceGroups, importGroups }) => { apolloProvider = createMockApollo([], { Query: { availableNamespaces: () => availableNamespacesFixture, bulkImportSourceGroups, }, Mutation: { - setTargetNamespace: jest.fn(), - setNewName: jest.fn(), - importGroup: jest.fn(), + importGroups, }, }); wrapper = mount(ImportTable, { propsData: { groupPathRegex: /.*/, + jobsPath: '/fake_job_path', sourceUrl: SOURCE_URL, - groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.', - }, - stubs: { - ...stubChildren(ImportTable), - GlSprintf: false, - GlDropdown: GlDropdownStub, - GlTable: false, }, - localVue, apolloProvider, }); }; + beforeAll(() => { + gon.api_version = 'v4'; + }); + + beforeEach(() => { + axiosMock = new MockAdapter(axios); + axiosMock.onGet(/.*\/exists$/, () => []).reply(200); + }); + afterEach(() => { wrapper.destroy(); }); - it('renders loading icon while performing request', async () => { - createComponent({ - bulkImportSourceGroups: () => new Promise(() => {}), + describe('loading state', () => { + it('renders loading icon while performing request', async () => { + createComponent({ + bulkImportSourceGroups: () => new Promise(() => {}), + }); + await waitForPromises(); + + expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); }); - await waitForPromises(); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); - }); + it('does not renders loading icon when request is completed', async () => { + createComponent({ + bulkImportSourceGroups: () => [], + }); + await waitForPromises(); - it('does not renders loading icon when request is completed', async () => { - createComponent({ - bulkImportSourceGroups: () => [], + expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); }); - await waitForPromises(); - - expect(wrapper.find(GlLoadingIcon).exists()).toBe(false); }); - it('renders message about empty state when no groups are available for import', async () => { - createComponent({ - bulkImportSourceGroups: () => ({ - nodes: [], - pageInfo: FAKE_PAGE_INFO, - }), - }); - await waitForPromises(); + describe('empty state', () => { + it('renders message about empty state when no groups are available for import', async () => { + createComponent({ + bulkImportSourceGroups: () => ({ + nodes: [], + pageInfo: FAKE_PAGE_INFO, + }), + }); + await waitForPromises(); - expect(wrapper.find(GlEmptyState).props().title).toBe('You have no groups to import'); + expect(wrapper.find(GlEmptyState).props().title).toBe('You have no groups to import'); + }); }); it('renders import row for each group in response', async () => { @@ -140,40 +136,51 @@ describe('import table', () => { expect(wrapper.text()).not.toContain('Showing 1-0'); }); - describe('converts row events to mutation invocations', () => { - beforeEach(() => { - createComponent({ - bulkImportSourceGroups: () => ({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO }), - }); - return waitForPromises(); + it('invokes importGroups mutation when row button is clicked', async () => { + createComponent({ + bulkImportSourceGroups: () => ({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO }), }); - it.each` - event | payload | mutation | variables - ${'update-target-namespace'} | ${'new-namespace'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'new-namespace', newName: 'group1' }} - ${'update-new-name'} | ${'new-name'} | ${setImportTargetMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'root', newName: 'new-name' }} - `('correctly maps $event to mutation', async ({ event, payload, mutation, variables }) => { - jest.spyOn(apolloProvider.defaultClient, 'mutate'); - wrapper.find(ImportTargetCell).vm.$emit(event, payload); - await waitForPromises(); - expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ - mutation, - variables, - }); - }); + jest.spyOn(apolloProvider.defaultClient, 'mutate'); - it('invokes importGroups mutation when row button is clicked', async () => { - jest.spyOn(apolloProvider.defaultClient, 'mutate'); + await waitForPromises(); - wrapper.findComponent(ImportActionsCell).vm.$emit('import-group'); - await waitForPromises(); - expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ - mutation: importGroupsMutation, - variables: { sourceGroupIds: [FAKE_GROUP.id] }, - }); + await findImportButtons()[0].trigger('click'); + expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: importGroupsMutation, + variables: { + importRequests: [ + { + newName: FAKE_GROUP.lastImportTarget.newName, + sourceGroupId: FAKE_GROUP.id, + targetNamespace: availableNamespacesFixture[0].fullPath, + }, + ], + }, }); }); + it('displays error if importing group fails', async () => { + createComponent({ + bulkImportSourceGroups: () => ({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO }), + importGroups: () => { + throw new Error(); + }, + }); + + axiosMock.onPost('/import/bulk_imports.json').reply(httpStatus.BAD_REQUEST); + + await waitForPromises(); + await findImportButtons()[0].trigger('click'); + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith( + expect.objectContaining({ + message: i18n.ERROR_IMPORT, + }), + ); + }); + describe('pagination', () => { const bulkImportSourceGroupsQueryMock = jest .fn() @@ -195,10 +202,10 @@ describe('import table', () => { }); it('updates page size when selected in Dropdown', async () => { - const otherOption = wrapper.findAllComponents(GlDropdownItem).at(1); + const otherOption = findPaginationDropdown().findAll('li p').at(1); expect(otherOption.text()).toMatchInterpolatedText('50 items per page'); - otherOption.vm.$emit('click'); + await otherOption.trigger('click'); await waitForPromises(); expect(findPaginationDropdownText()).toMatchInterpolatedText('50 items per page'); @@ -247,7 +254,11 @@ describe('import table', () => { return waitForPromises(); }); - const findFilterInput = () => wrapper.find(GlSearchBoxByClick); + const setFilter = (value) => { + const input = wrapper.find('input[placeholder="Filter by source group"]'); + input.setValue(value); + return input.trigger('keydown.enter'); + }; it('properly passes filter to graphql query when search box is submitted', async () => { createComponent({ @@ -256,7 +267,7 @@ describe('import table', () => { await waitForPromises(); const FILTER_VALUE = 'foo'; - findFilterInput().vm.$emit('submit', FILTER_VALUE); + await setFilter(FILTER_VALUE); await waitForPromises(); expect(bulkImportSourceGroupsQueryMock).toHaveBeenCalledWith( @@ -274,7 +285,7 @@ describe('import table', () => { await waitForPromises(); const FILTER_VALUE = 'foo'; - findFilterInput().vm.$emit('submit', FILTER_VALUE); + await setFilter(FILTER_VALUE); await waitForPromises(); expect(wrapper.text()).toContain('Showing 1-1 of 40 groups matching filter "foo" from'); @@ -282,12 +293,14 @@ describe('import table', () => { it('properly resets filter in graphql query when search box is cleared', async () => { const FILTER_VALUE = 'foo'; - findFilterInput().vm.$emit('submit', FILTER_VALUE); + await setFilter(FILTER_VALUE); await waitForPromises(); bulkImportSourceGroupsQueryMock.mockClear(); await apolloProvider.defaultClient.resetStore(); - findFilterInput().vm.$emit('clear'); + + await setFilter(''); + await waitForPromises(); expect(bulkImportSourceGroupsQueryMock).toHaveBeenCalledWith( @@ -320,8 +333,8 @@ describe('import table', () => { }), }); await waitForPromises(); - wrapper.find(GlTable).vm.$emit('row-selected', [FAKE_GROUPS[0]]); - await nextTick(); + + await selectRow(0); expect(findImportSelectedButton().props().disabled).toBe(false); }); @@ -337,7 +350,7 @@ describe('import table', () => { }); await waitForPromises(); - findTable().selectRow(0); + await selectRow(0); await nextTick(); expect(findImportSelectedButton().props().disabled).toBe(true); @@ -348,7 +361,6 @@ describe('import table', () => { generateFakeEntry({ id: 2, status: STATUSES.NONE, - validation_errors: [{ field: 'new_name', message: 'FAKE_VALIDATION_ERROR' }], }), ]; @@ -360,9 +372,9 @@ describe('import table', () => { }); await waitForPromises(); - // TODO: remove this ugly approach when - // issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531 - findTable().selectRow(0); + await wrapper.find('tbody input[aria-label="New name"]').setValue(''); + jest.runOnlyPendingTimers(); + await selectRow(0); await nextTick(); expect(findImportSelectedButton().props().disabled).toBe(true); @@ -384,15 +396,28 @@ describe('import table', () => { jest.spyOn(apolloProvider.defaultClient, 'mutate'); await waitForPromises(); - findTable().selectRow(0); - findTable().selectRow(1); + await selectRow(0); + await selectRow(1); await nextTick(); - findImportSelectedButton().vm.$emit('click'); + await findImportSelectedButton().trigger('click'); expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({ mutation: importGroupsMutation, - variables: { sourceGroupIds: [NEW_GROUPS[0].id, NEW_GROUPS[1].id] }, + variables: { + importRequests: [ + { + targetNamespace: availableNamespacesFixture[0].fullPath, + newName: NEW_GROUPS[0].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[0].id, + }, + { + targetNamespace: availableNamespacesFixture[0].fullPath, + newName: NEW_GROUPS[1].lastImportTarget.newName, + sourceGroupId: NEW_GROUPS[1].id, + }, + ], + }, }); }); }); |