summaryrefslogtreecommitdiff
path: root/spec/frontend/import_entities
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/import_entities')
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js120
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js (renamed from spec/frontend/import_entities/import_groups/components/import_table_row_spec.js)155
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js88
3 files changed, 157 insertions, 206 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 99ef6d9a7fb..bbd8463e685 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
@@ -3,21 +3,22 @@ import {
GlEmptyState,
GlLoadingIcon,
GlSearchBoxByClick,
- GlSprintf,
GlDropdown,
GlDropdownItem,
+ GlTable,
} from '@gitlab/ui';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
+import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
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 { STATUSES } from '~/import_entities/constants';
import ImportTable from '~/import_entities/import_groups/components/import_table.vue';
-import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.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 setNewNameMutation from '~/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql';
-import setTargetNamespaceMutation from '~/import_entities/import_groups/graphql/mutations/set_target_namespace.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';
@@ -41,10 +42,15 @@ describe('import table', () => {
];
const FAKE_PAGE_INFO = { page: 1, perPage: 20, total: 40, totalPages: 2 };
- const findImportAllButton = () => wrapper.find('h1').find(GlButton);
+ const findImportSelectedButton = () =>
+ wrapper.findAllComponents(GlButton).wrappers.find((w) => w.text() === 'Import selected');
const findPaginationDropdown = () => wrapper.findComponent(GlDropdown);
const findPaginationDropdownText = () => findPaginationDropdown().find({ ref: 'text' }).text();
+ // TODO: remove this ugly approach when
+ // issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531
+ const findTable = () => wrapper.vm.getTableRef();
+
const createComponent = ({ bulkImportSourceGroups }) => {
apolloProvider = createMockApollo([], {
Query: {
@@ -58,14 +64,17 @@ describe('import table', () => {
},
});
- wrapper = shallowMount(ImportTable, {
+ wrapper = mount(ImportTable, {
propsData: {
groupPathRegex: /.*/,
sourceUrl: SOURCE_URL,
+ groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.',
},
stubs: {
- GlSprintf,
+ ...stubChildren(ImportTable),
+ GlSprintf: false,
GlDropdown: GlDropdownStub,
+ GlTable: false,
},
localVue,
apolloProvider,
@@ -115,7 +124,7 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.findAll(ImportTableRow)).toHaveLength(FAKE_GROUPS.length);
+ expect(wrapper.findAll('tbody tr')).toHaveLength(FAKE_GROUPS.length);
});
it('does not render status string when result list is empty', async () => {
@@ -139,19 +148,32 @@ describe('import table', () => {
});
it.each`
- event | payload | mutation | variables
- ${'update-target-namespace'} | ${'new-namespace'} | ${setTargetNamespaceMutation} | ${{ sourceGroupId: FAKE_GROUP.id, targetNamespace: 'new-namespace' }}
- ${'update-new-name'} | ${'new-name'} | ${setNewNameMutation} | ${{ sourceGroupId: FAKE_GROUP.id, newName: 'new-name' }}
- ${'import-group'} | ${undefined} | ${importGroupsMutation} | ${{ sourceGroupIds: [FAKE_GROUP.id] }}
+ 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(ImportTableRow).vm.$emit(event, payload);
+ wrapper.find(ImportTargetCell).vm.$emit(event, payload);
await waitForPromises();
expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
mutation,
variables,
});
});
+
+ it('invokes importGroups mutation when row button is clicked', async () => {
+ jest.spyOn(apolloProvider.defaultClient, 'mutate');
+ const triggerImportButton = wrapper
+ .findAllComponents(GlButton)
+ .wrappers.find((w) => w.text() === 'Import');
+
+ triggerImportButton.vm.$emit('click');
+ await waitForPromises();
+ expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: importGroupsMutation,
+ variables: { sourceGroupIds: [FAKE_GROUP.id] },
+ });
+ });
});
describe('pagination', () => {
@@ -279,16 +301,20 @@ describe('import table', () => {
});
});
- describe('import all button', () => {
- it('does not exists when no groups available', () => {
+ describe('bulk operations', () => {
+ it('import selected button is disabled when no groups selected', async () => {
createComponent({
- bulkImportSourceGroups: () => new Promise(() => {}),
+ bulkImportSourceGroups: () => ({
+ nodes: FAKE_GROUPS,
+ pageInfo: FAKE_PAGE_INFO,
+ }),
});
+ await waitForPromises();
- expect(findImportAllButton().exists()).toBe(false);
+ expect(findImportSelectedButton().props().disabled).toBe(true);
});
- it('exists when groups are available for import', async () => {
+ it('import selected button is enabled when groups were selected for import', async () => {
createComponent({
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
@@ -296,16 +322,14 @@ describe('import table', () => {
}),
});
await waitForPromises();
+ wrapper.find(GlTable).vm.$emit('row-selected', [FAKE_GROUPS[0]]);
+ await nextTick();
- expect(findImportAllButton().exists()).toBe(true);
+ expect(findImportSelectedButton().props().disabled).toBe(false);
});
- it('counts only not-imported groups', async () => {
- const NEW_GROUPS = [
- generateFakeEntry({ id: 1, status: STATUSES.NONE }),
- generateFakeEntry({ id: 2, status: STATUSES.NONE }),
- generateFakeEntry({ id: 3, status: STATUSES.FINISHED }),
- ];
+ it('does not allow selecting already started groups', async () => {
+ const NEW_GROUPS = [generateFakeEntry({ id: 1, status: STATUSES.FINISHED })];
createComponent({
bulkImportSourceGroups: () => ({
@@ -315,17 +339,41 @@ describe('import table', () => {
});
await waitForPromises();
- expect(findImportAllButton().text()).toMatchInterpolatedText('Import 2 groups');
+ findTable().selectRow(0);
+ await nextTick();
+
+ expect(findImportSelectedButton().props().disabled).toBe(true);
});
- it('disables button when any group has validation errors', async () => {
+ it('does not allow selecting groups with validation errors', async () => {
const NEW_GROUPS = [
- generateFakeEntry({ id: 1, status: STATUSES.NONE }),
generateFakeEntry({
id: 2,
status: STATUSES.NONE,
- validation_errors: [{ field: 'new_name', message: 'test validation error' }],
+ validation_errors: [{ field: 'new_name', message: 'FAKE_VALIDATION_ERROR' }],
}),
+ ];
+
+ createComponent({
+ bulkImportSourceGroups: () => ({
+ nodes: NEW_GROUPS,
+ pageInfo: FAKE_PAGE_INFO,
+ }),
+ });
+ await waitForPromises();
+
+ // TODO: remove this ugly approach when
+ // issue: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1531
+ findTable().selectRow(0);
+ await nextTick();
+
+ expect(findImportSelectedButton().props().disabled).toBe(true);
+ });
+
+ it('invokes importGroups mutation when import selected button is clicked', async () => {
+ const NEW_GROUPS = [
+ generateFakeEntry({ id: 1, status: STATUSES.NONE }),
+ generateFakeEntry({ id: 2, status: STATUSES.NONE }),
generateFakeEntry({ id: 3, status: STATUSES.FINISHED }),
];
@@ -335,9 +383,19 @@ describe('import table', () => {
pageInfo: FAKE_PAGE_INFO,
}),
});
+ jest.spyOn(apolloProvider.defaultClient, 'mutate');
await waitForPromises();
- expect(findImportAllButton().props().disabled).toBe(true);
+ findTable().selectRow(0);
+ findTable().selectRow(1);
+ await nextTick();
+
+ findImportSelectedButton().vm.$emit('click');
+
+ expect(apolloProvider.defaultClient.mutate).toHaveBeenCalledWith({
+ mutation: importGroupsMutation,
+ variables: { sourceGroupIds: [NEW_GROUPS[0].id, NEW_GROUPS[1].id] },
+ });
});
});
});
diff --git a/spec/frontend/import_entities/import_groups/components/import_table_row_spec.js b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
index 654a8fd00d3..8231297e594 100644
--- a/spec/frontend/import_entities/import_groups/components/import_table_row_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
@@ -2,19 +2,13 @@ import { GlButton, GlDropdownItem, GlLink, GlFormInput } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import { STATUSES } from '~/import_entities/constants';
-import ImportTableRow from '~/import_entities/import_groups/components/import_table_row.vue';
-import addValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/add_validation_error.mutation.graphql';
-import removeValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/remove_validation_error.mutation.graphql';
-import groupAndProjectQuery from '~/import_entities/import_groups/graphql/queries/groupAndProject.query.graphql';
+import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue';
import { availableNamespacesFixture } from '../graphql/fixtures';
Vue.use(VueApollo);
-const { i18n: I18N } = ImportTableRow;
-
const getFakeGroup = (status) => ({
web_url: 'https://fake.host/',
full_path: 'fake_group_1',
@@ -28,48 +22,23 @@ const getFakeGroup = (status) => ({
progress: { status },
});
-const EXISTING_GROUP_TARGET_NAMESPACE = 'existing-group';
-const EXISTING_GROUP_PATH = 'existing-path';
-const EXISTING_PROJECT_PATH = 'existing-project-path';
-
-describe('import table row', () => {
+describe('import target cell', () => {
let wrapper;
- let apolloProvider;
let group;
const findByText = (cmp, text) => {
return wrapper.findAll(cmp).wrappers.find((node) => node.text().indexOf(text) === 0);
};
- const findImportButton = () => findByText(GlButton, 'Import');
const findNameInput = () => wrapper.find(GlFormInput);
const findNamespaceDropdown = () => wrapper.find(ImportGroupDropdown);
const createComponent = (props) => {
- apolloProvider = createMockApollo([
- [
- groupAndProjectQuery,
- ({ fullPath }) => {
- const existingGroup =
- fullPath === `${EXISTING_GROUP_TARGET_NAMESPACE}/${EXISTING_GROUP_PATH}`
- ? { id: 1 }
- : null;
-
- const existingProject =
- fullPath === `${EXISTING_GROUP_TARGET_NAMESPACE}/${EXISTING_PROJECT_PATH}`
- ? { id: 1 }
- : null;
-
- return Promise.resolve({ data: { existingGroup, existingProject } });
- },
- ],
- ]);
-
- wrapper = shallowMount(ImportTableRow, {
- apolloProvider,
+ wrapper = shallowMount(ImportTargetCell, {
stubs: { ImportGroupDropdown },
propsData: {
availableNamespaces: availableNamespacesFixture,
groupPathRegex: /.*/,
+ groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.',
...props,
},
});
@@ -86,14 +55,10 @@ describe('import table row', () => {
createComponent({ group });
});
- it.each`
- selector | sourceEvent | payload | event
- ${findNameInput} | ${'input'} | ${'demo'} | ${'update-new-name'}
- ${findImportButton} | ${'click'} | ${undefined} | ${'import-group'}
- `('invokes $event', ({ selector, sourceEvent, payload, event }) => {
- selector().vm.$emit(sourceEvent, payload);
- expect(wrapper.emitted(event)).toBeDefined();
- expect(wrapper.emitted(event)[0][0]).toBe(payload);
+ it('invokes $event', () => {
+ findNameInput().vm.$emit('input', 'demo');
+ expect(wrapper.emitted('update-new-name')).toBeDefined();
+ expect(wrapper.emitted('update-new-name')[0][0]).toBe('demo');
});
it('emits update-target-namespace when dropdown option is clicked', () => {
@@ -113,10 +78,6 @@ describe('import table row', () => {
createComponent({ group });
});
- it('renders Import button', () => {
- expect(findByText(GlButton, 'Import').exists()).toBe(true);
- });
-
it('renders namespace dropdown as not disabled', () => {
expect(findNamespaceDropdown().attributes('disabled')).toBe(undefined);
});
@@ -198,7 +159,9 @@ describe('import table row', () => {
groupPathRegex: /^[a-zA-Z]+$/,
});
- expect(wrapper.text()).toContain('Please choose a group URL with no special characters.');
+ expect(wrapper.text()).toContain(
+ 'Please choose a group URL with no special characters or spaces.',
+ );
});
it('reports invalid group name if relevant validation error exists', async () => {
@@ -221,101 +184,5 @@ describe('import table row', () => {
expect(wrapper.text()).toContain(FAKE_ERROR_MESSAGE);
});
-
- it('sets validation error when targetting existing group', async () => {
- const testGroup = getFakeGroup(STATUSES.NONE);
-
- createComponent({
- group: {
- ...testGroup,
- import_target: {
- target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
- new_name: EXISTING_GROUP_PATH,
- },
- },
- });
-
- jest.spyOn(wrapper.vm.$apollo, 'mutate');
-
- jest.runOnlyPendingTimers();
- await nextTick();
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: addValidationErrorMutation,
- variables: {
- field: 'new_name',
- message: I18N.NAME_ALREADY_EXISTS,
- sourceGroupId: testGroup.id,
- },
- });
- });
-
- it('sets validation error when targetting existing project', async () => {
- const testGroup = getFakeGroup(STATUSES.NONE);
-
- createComponent({
- group: {
- ...testGroup,
- import_target: {
- target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
- new_name: EXISTING_PROJECT_PATH,
- },
- },
- });
-
- jest.spyOn(wrapper.vm.$apollo, 'mutate');
-
- jest.runOnlyPendingTimers();
- await nextTick();
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: addValidationErrorMutation,
- variables: {
- field: 'new_name',
- message: I18N.NAME_ALREADY_EXISTS,
- sourceGroupId: testGroup.id,
- },
- });
- });
-
- it('clears validation error when target is updated', async () => {
- const testGroup = getFakeGroup(STATUSES.NONE);
-
- createComponent({
- group: {
- ...testGroup,
- import_target: {
- target_namespace: EXISTING_GROUP_TARGET_NAMESPACE,
- new_name: EXISTING_PROJECT_PATH,
- },
- },
- });
-
- jest.runOnlyPendingTimers();
- await nextTick();
-
- jest.spyOn(wrapper.vm.$apollo, 'mutate');
-
- await wrapper.setProps({
- group: {
- ...testGroup,
- import_target: {
- target_namespace: 'valid_namespace',
- new_name: 'valid_path',
- },
- },
- });
-
- jest.runOnlyPendingTimers();
- await nextTick();
-
- expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
- mutation: removeValidationErrorMutation,
- variables: {
- field: 'new_name',
- sourceGroupId: testGroup.id,
- },
- });
- });
});
});
diff --git a/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
index ef83c9ebbc4..ec50dfd037f 100644
--- a/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
+++ b/spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js
@@ -12,12 +12,12 @@ import addValidationErrorMutation from '~/import_entities/import_groups/graphql/
import importGroupsMutation from '~/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql';
import removeValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/remove_validation_error.mutation.graphql';
import setImportProgressMutation from '~/import_entities/import_groups/graphql/mutations/set_import_progress.mutation.graphql';
-import setNewNameMutation from '~/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql';
-import setTargetNamespaceMutation from '~/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql';
+import setImportTargetMutation from '~/import_entities/import_groups/graphql/mutations/set_import_target.mutation.graphql';
import updateImportStatusMutation from '~/import_entities/import_groups/graphql/mutations/update_import_status.mutation.graphql';
import availableNamespacesQuery from '~/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql';
import bulkImportSourceGroupQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_group.query.graphql';
import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
+import groupAndProjectQuery from '~/import_entities/import_groups/graphql/queries/group_and_project.query.graphql';
import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
import axios from '~/lib/utils/axios_utils';
@@ -38,18 +38,29 @@ const FAKE_ENDPOINTS = {
jobs: '/fake_jobs',
};
+const FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER = jest.fn().mockResolvedValue({
+ data: {
+ existingGroup: null,
+ existingProject: null,
+ },
+});
+
describe('Bulk import resolvers', () => {
let axiosMockAdapter;
let client;
const createClient = (extraResolverArgs) => {
- return createMockClient({
+ const mockedClient = createMockClient({
cache: new InMemoryCache({
fragmentMatcher: { match: () => true },
addTypename: false,
}),
resolvers: createResolvers({ endpoints: FAKE_ENDPOINTS, ...extraResolverArgs }),
});
+
+ mockedClient.setRequestHandler(groupAndProjectQuery, FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER);
+
+ return mockedClient;
};
beforeEach(() => {
@@ -196,6 +207,12 @@ describe('Bulk import resolvers', () => {
const [statusPoller] = StatusPoller.mock.instances;
expect(statusPoller.startPolling).toHaveBeenCalled();
});
+
+ it('requests validation status when request completes', async () => {
+ expect(FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER).not.toHaveBeenCalled();
+ jest.runOnlyPendingTimers();
+ expect(FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER).toHaveBeenCalled();
+ });
});
it.each`
@@ -256,40 +273,49 @@ describe('Bulk import resolvers', () => {
});
});
- it('setTargetNamespaces updates group target namespace', async () => {
- const NEW_TARGET_NAMESPACE = 'target';
- const {
- data: {
- setTargetNamespace: {
- id: idInResponse,
- import_target: { target_namespace: namespaceInResponse },
+ describe('setImportTarget', () => {
+ it('updates group target namespace and name', async () => {
+ const NEW_TARGET_NAMESPACE = 'target';
+ const NEW_NAME = 'new';
+
+ const {
+ data: {
+ setImportTarget: {
+ id: idInResponse,
+ import_target: { target_namespace: namespaceInResponse, new_name: newNameInResponse },
+ },
},
- },
- } = await client.mutate({
- mutation: setTargetNamespaceMutation,
- variables: { sourceGroupId: GROUP_ID, targetNamespace: NEW_TARGET_NAMESPACE },
+ } = await client.mutate({
+ mutation: setImportTargetMutation,
+ variables: {
+ sourceGroupId: GROUP_ID,
+ targetNamespace: NEW_TARGET_NAMESPACE,
+ newName: NEW_NAME,
+ },
+ });
+
+ expect(idInResponse).toBe(GROUP_ID);
+ expect(namespaceInResponse).toBe(NEW_TARGET_NAMESPACE);
+ expect(newNameInResponse).toBe(NEW_NAME);
});
- expect(idInResponse).toBe(GROUP_ID);
- expect(namespaceInResponse).toBe(NEW_TARGET_NAMESPACE);
- });
+ it('invokes validation', async () => {
+ const NEW_TARGET_NAMESPACE = 'target';
+ const NEW_NAME = 'new';
- it('setNewName updates group target name', async () => {
- const NEW_NAME = 'new';
- const {
- data: {
- setNewName: {
- id: idInResponse,
- import_target: { new_name: nameInResponse },
+ await client.mutate({
+ mutation: setImportTargetMutation,
+ variables: {
+ sourceGroupId: GROUP_ID,
+ targetNamespace: NEW_TARGET_NAMESPACE,
+ newName: NEW_NAME,
},
- },
- } = await client.mutate({
- mutation: setNewNameMutation,
- variables: { sourceGroupId: GROUP_ID, newName: NEW_NAME },
- });
+ });
- expect(idInResponse).toBe(GROUP_ID);
- expect(nameInResponse).toBe(NEW_NAME);
+ expect(FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER).toHaveBeenCalledWith({
+ fullPath: `${NEW_TARGET_NAMESPACE}/${NEW_NAME}`,
+ });
+ });
});
describe('importGroup', () => {