Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/import_entities/import_groups')
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js33
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_source_cell_spec.js27
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js235
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js96
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js442
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/fixtures.js42
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/local_storage_cache_spec.js61
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js64
-rw-r--r--spec/frontend/import_entities/import_groups/services/status_poller_spec.js (renamed from spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js)9
9 files changed, 354 insertions, 655 deletions
diff --git a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
index 60f0780fdb3..cd56f573011 100644
--- a/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_actions_cell_spec.js
@@ -1,8 +1,6 @@
import { GlButton, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import { STATUSES } from '~/import_entities/constants';
import ImportActionsCell from '~/import_entities/import_groups/components/import_actions_cell.vue';
-import { generateFakeEntry } from '../graphql/fixtures';
describe('import actions cell', () => {
let wrapper;
@@ -10,7 +8,9 @@ describe('import actions cell', () => {
const createComponent = (props) => {
wrapper = shallowMount(ImportActionsCell, {
propsData: {
- groupPathRegex: /^[a-zA-Z]+$/,
+ isFinished: false,
+ isAvailableForImport: false,
+ isInvalid: false,
...props,
},
});
@@ -20,10 +20,9 @@ describe('import actions cell', () => {
wrapper.destroy();
});
- describe('when import status is NONE', () => {
+ describe('when group is available for import', () => {
beforeEach(() => {
- const group = generateFakeEntry({ id: 1, status: STATUSES.NONE });
- createComponent({ group });
+ createComponent({ isAvailableForImport: true });
});
it('renders import button', () => {
@@ -37,10 +36,9 @@ describe('import actions cell', () => {
});
});
- describe('when import status is FINISHED', () => {
+ describe('when group is finished', () => {
beforeEach(() => {
- const group = generateFakeEntry({ id: 1, status: STATUSES.FINISHED });
- createComponent({ group });
+ createComponent({ isAvailableForImport: true, isFinished: true });
});
it('renders re-import button', () => {
@@ -58,29 +56,22 @@ describe('import actions cell', () => {
});
});
- it('does not render import button when group import is in progress', () => {
- const group = generateFakeEntry({ id: 1, status: STATUSES.STARTED });
- createComponent({ group });
+ it('does not render import button when group is not available for import', () => {
+ createComponent({ isAvailableForImport: false });
const button = wrapper.findComponent(GlButton);
expect(button.exists()).toBe(false);
});
- it('renders import button as disabled when there are validation errors', () => {
- const group = generateFakeEntry({
- id: 1,
- status: STATUSES.NONE,
- validation_errors: [{ field: 'new_name', message: 'something ' }],
- });
- createComponent({ group });
+ it('renders import button as disabled when group is invalid', () => {
+ createComponent({ isInvalid: true, isAvailableForImport: true });
const button = wrapper.findComponent(GlButton);
expect(button.props().disabled).toBe(true);
});
it('emits import-group event when import button is clicked', () => {
- const group = generateFakeEntry({ id: 1, status: STATUSES.NONE });
- createComponent({ group });
+ createComponent({ isAvailableForImport: true });
const button = wrapper.findComponent(GlButton);
button.vm.$emit('click');
diff --git a/spec/frontend/import_entities/import_groups/components/import_source_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_source_cell_spec.js
index 2a56efd1cbb..f2735d86493 100644
--- a/spec/frontend/import_entities/import_groups/components/import_source_cell_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_source_cell_spec.js
@@ -4,6 +4,11 @@ import { STATUSES } from '~/import_entities/constants';
import ImportSourceCell from '~/import_entities/import_groups/components/import_source_cell.vue';
import { generateFakeEntry } from '../graphql/fixtures';
+const generateFakeTableEntry = ({ flags = {}, ...entry }) => ({
+ ...generateFakeEntry(entry),
+ flags,
+});
+
describe('import source cell', () => {
let wrapper;
let group;
@@ -23,14 +28,14 @@ describe('import source cell', () => {
describe('when group status is NONE', () => {
beforeEach(() => {
- group = generateFakeEntry({ id: 1, status: STATUSES.NONE });
+ group = generateFakeTableEntry({ id: 1, status: STATUSES.NONE });
createComponent({ group });
});
it('renders link to a group', () => {
const link = wrapper.findComponent(GlLink);
- expect(link.attributes().href).toBe(group.web_url);
- expect(link.text()).toContain(group.full_path);
+ expect(link.attributes().href).toBe(group.webUrl);
+ expect(link.text()).toContain(group.fullPath);
});
it('does not render last imported line', () => {
@@ -40,20 +45,24 @@ describe('import source cell', () => {
describe('when group status is FINISHED', () => {
beforeEach(() => {
- group = generateFakeEntry({ id: 1, status: STATUSES.FINISHED });
+ group = generateFakeTableEntry({
+ id: 1,
+ status: STATUSES.FINISHED,
+ flags: {
+ isFinished: true,
+ },
+ });
createComponent({ group });
});
it('renders link to a group', () => {
const link = wrapper.findComponent(GlLink);
- expect(link.attributes().href).toBe(group.web_url);
- expect(link.text()).toContain(group.full_path);
+ expect(link.attributes().href).toBe(group.webUrl);
+ expect(link.text()).toContain(group.fullPath);
});
it('renders last imported line', () => {
- expect(wrapper.text()).toMatchInterpolatedText(
- 'fake_group_1 Last imported to root/last-group1',
- );
+ expect(wrapper.text()).toMatchInterpolatedText('fake_group_1 Last imported to root/group1');
});
});
});
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,
+ },
+ ],
+ },
});
});
});
diff --git a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
index be83a61841f..3c2367e22f5 100644
--- a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
+++ b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js
@@ -3,20 +3,20 @@ import { shallowMount } from '@vue/test-utils';
import ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue';
import { STATUSES } from '~/import_entities/constants';
import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue';
-import { availableNamespacesFixture } from '../graphql/fixtures';
-
-const getFakeGroup = (status) => ({
- web_url: 'https://fake.host/',
- full_path: 'fake_group_1',
- full_name: 'fake_name_1',
- import_target: {
- target_namespace: 'root',
- new_name: 'group1',
- },
- id: 1,
- validation_errors: [],
- progress: { status },
-});
+import { generateFakeEntry, availableNamespacesFixture } from '../graphql/fixtures';
+
+const generateFakeTableEntry = ({ flags = {}, ...config }) => {
+ const entry = generateFakeEntry(config);
+
+ return {
+ ...entry,
+ importTarget: {
+ targetNamespace: availableNamespacesFixture[0],
+ newName: entry.lastImportTarget.newName,
+ },
+ flags,
+ };
+};
describe('import target cell', () => {
let wrapper;
@@ -31,7 +31,6 @@ describe('import target cell', () => {
propsData: {
availableNamespaces: availableNamespacesFixture,
groupPathRegex: /.*/,
- groupUrlErrorMessage: 'Please choose a group URL with no special characters or spaces.',
...props,
},
});
@@ -44,11 +43,11 @@ describe('import target cell', () => {
describe('events', () => {
beforeEach(() => {
- group = getFakeGroup(STATUSES.NONE);
+ group = generateFakeTableEntry({ id: 1, status: STATUSES.NONE });
createComponent({ group });
});
- it('invokes $event', () => {
+ it('emits update-new-name when input value is changed', () => {
findNameInput().vm.$emit('input', 'demo');
expect(wrapper.emitted('update-new-name')).toBeDefined();
expect(wrapper.emitted('update-new-name')[0][0]).toBe('demo');
@@ -56,18 +55,23 @@ describe('import target cell', () => {
it('emits update-target-namespace when dropdown option is clicked', () => {
const dropdownItem = findNamespaceDropdown().findAllComponents(GlDropdownItem).at(2);
- const dropdownItemText = dropdownItem.text();
dropdownItem.vm.$emit('click');
expect(wrapper.emitted('update-target-namespace')).toBeDefined();
- expect(wrapper.emitted('update-target-namespace')[0][0]).toBe(dropdownItemText);
+ expect(wrapper.emitted('update-target-namespace')[0][0]).toBe(availableNamespacesFixture[1]);
});
});
describe('when entity status is NONE', () => {
beforeEach(() => {
- group = getFakeGroup(STATUSES.NONE);
+ group = generateFakeTableEntry({
+ id: 1,
+ status: STATUSES.NONE,
+ flags: {
+ isAvailableForImport: true,
+ },
+ });
createComponent({ group });
});
@@ -78,7 +82,7 @@ describe('import target cell', () => {
it('renders only no parent option if available namespaces list is empty', () => {
createComponent({
- group: getFakeGroup(STATUSES.NONE),
+ group: generateFakeTableEntry({ id: 1, status: STATUSES.NONE }),
availableNamespaces: [],
});
@@ -92,7 +96,7 @@ describe('import target cell', () => {
it('renders both no parent option and available namespaces list when available namespaces list is not empty', () => {
createComponent({
- group: getFakeGroup(STATUSES.NONE),
+ group: generateFakeTableEntry({ id: 1, status: STATUSES.NONE }),
availableNamespaces: availableNamespacesFixture,
});
@@ -104,9 +108,12 @@ describe('import target cell', () => {
expect(rest).toHaveLength(availableNamespacesFixture.length);
});
- describe('when entity status is SCHEDULING', () => {
+ describe('when entity is not available for import', () => {
beforeEach(() => {
- group = getFakeGroup(STATUSES.SCHEDULING);
+ group = generateFakeTableEntry({
+ id: 1,
+ flags: { isAvailableForImport: false },
+ });
createComponent({ group });
});
@@ -115,9 +122,9 @@ describe('import target cell', () => {
});
});
- describe('when entity status is FINISHED', () => {
+ describe('when entity is available for import', () => {
beforeEach(() => {
- group = getFakeGroup(STATUSES.FINISHED);
+ group = generateFakeTableEntry({ id: 1, flags: { isAvailableForImport: true } });
createComponent({ group });
});
@@ -125,41 +132,4 @@ describe('import target cell', () => {
expect(findNamespaceDropdown().attributes('disabled')).toBe(undefined);
});
});
-
- describe('validations', () => {
- it('reports invalid group name when name is not matching regex', () => {
- createComponent({
- group: {
- ...getFakeGroup(STATUSES.NONE),
- import_target: {
- target_namespace: 'root',
- new_name: 'very`bad`name',
- },
- },
- groupPathRegex: /^[a-zA-Z]+$/,
- });
-
- 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 () => {
- const FAKE_ERROR_MESSAGE = 'fake error';
-
- createComponent({
- group: {
- ...getFakeGroup(STATUSES.NONE),
- validation_errors: [
- {
- field: 'new_name',
- message: FAKE_ERROR_MESSAGE,
- },
- ],
- },
- });
-
- expect(wrapper.text()).toContain(FAKE_ERROR_MESSAGE);
- });
- });
});
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 e1d65095888..f3447494578 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
@@ -2,32 +2,27 @@ import { InMemoryCache } from 'apollo-cache-inmemory';
import MockAdapter from 'axios-mock-adapter';
import { createMockClient } from 'mock-apollo-client';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
import { STATUSES } from '~/import_entities/constants';
import {
clientTypenames,
createResolvers,
} from '~/import_entities/import_groups/graphql/client_factory';
-import addValidationErrorMutation from '~/import_entities/import_groups/graphql/mutations/add_validation_error.mutation.graphql';
+import { LocalStorageCache } from '~/import_entities/import_groups/graphql/services/local_storage_cache';
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 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';
import httpStatus from '~/lib/utils/http_status';
import { statusEndpointFixture, availableNamespacesFixture } from './fixtures';
jest.mock('~/flash');
-jest.mock('~/import_entities/import_groups/graphql/services/status_poller', () => ({
- StatusPoller: jest.fn().mockImplementation(function mock() {
- this.startPolling = jest.fn();
+jest.mock('~/import_entities/import_groups/graphql/services/local_storage_cache', () => ({
+ LocalStorageCache: jest.fn().mockImplementation(function mock() {
+ this.get = jest.fn();
+ this.set = jest.fn();
+ this.updateStatusByJobId = jest.fn();
}),
}));
@@ -38,13 +33,6 @@ 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;
@@ -58,14 +46,28 @@ describe('Bulk import resolvers', () => {
resolvers: createResolvers({ endpoints: FAKE_ENDPOINTS, ...extraResolverArgs }),
});
- mockedClient.setRequestHandler(groupAndProjectQuery, FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER);
-
return mockedClient;
};
- beforeEach(() => {
+ let results;
+ beforeEach(async () => {
axiosMockAdapter = new MockAdapter(axios);
client = createClient();
+
+ axiosMockAdapter.onGet(FAKE_ENDPOINTS.status).reply(httpStatus.OK, statusEndpointFixture);
+ axiosMockAdapter.onGet(FAKE_ENDPOINTS.availableNamespaces).reply(
+ httpStatus.OK,
+ availableNamespacesFixture.map((ns) => ({
+ id: ns.id,
+ full_path: ns.fullPath,
+ })),
+ );
+
+ client.watchQuery({ query: bulkImportSourceGroupsQuery }).subscribe(({ data }) => {
+ results = data.bulkImportSourceGroups.nodes;
+ });
+
+ return waitForPromises();
});
afterEach(() => {
@@ -74,104 +76,41 @@ describe('Bulk import resolvers', () => {
describe('queries', () => {
describe('availableNamespaces', () => {
- let results;
-
+ let namespacesResults;
beforeEach(async () => {
- axiosMockAdapter
- .onGet(FAKE_ENDPOINTS.availableNamespaces)
- .reply(httpStatus.OK, availableNamespacesFixture);
-
const response = await client.query({ query: availableNamespacesQuery });
- results = response.data.availableNamespaces;
+ namespacesResults = response.data.availableNamespaces;
});
it('mirrors REST endpoint response fields', () => {
const extractRelevantFields = (obj) => ({ id: obj.id, full_path: obj.full_path });
- expect(results.map(extractRelevantFields)).toStrictEqual(
+ expect(namespacesResults.map(extractRelevantFields)).toStrictEqual(
availableNamespacesFixture.map(extractRelevantFields),
);
});
});
- describe('bulkImportSourceGroup', () => {
- beforeEach(async () => {
- axiosMockAdapter.onGet(FAKE_ENDPOINTS.status).reply(httpStatus.OK, statusEndpointFixture);
- axiosMockAdapter
- .onGet(FAKE_ENDPOINTS.availableNamespaces)
- .reply(httpStatus.OK, availableNamespacesFixture);
-
- return client.query({
- query: bulkImportSourceGroupsQuery,
- });
- });
-
- it('returns group', async () => {
- const { id } = statusEndpointFixture.importable_data[0];
- const {
- data: { bulkImportSourceGroup: group },
- } = await client.query({
- query: bulkImportSourceGroupQuery,
- variables: { id: id.toString() },
- });
-
- expect(group).toMatchObject(statusEndpointFixture.importable_data[0]);
- });
- });
-
describe('bulkImportSourceGroups', () => {
- let results;
-
- beforeEach(async () => {
- axiosMockAdapter.onGet(FAKE_ENDPOINTS.status).reply(httpStatus.OK, statusEndpointFixture);
- axiosMockAdapter
- .onGet(FAKE_ENDPOINTS.availableNamespaces)
- .reply(httpStatus.OK, availableNamespacesFixture);
- });
-
it('respects cached import state when provided by group manager', async () => {
- const FAKE_JOB_ID = '1';
- const FAKE_STATUS = 'DEMO_STATUS';
- const FAKE_IMPORT_TARGET = {
- new_name: 'test-name',
- target_namespace: 'test-namespace',
+ const [localStorageCache] = LocalStorageCache.mock.instances;
+ const CACHED_DATA = {
+ progress: {
+ id: 'DEMO',
+ status: 'cached',
+ },
};
- const TARGET_INDEX = 0;
+ localStorageCache.get.mockReturnValueOnce(CACHED_DATA);
- const clientWithMockedManager = createClient({
- GroupsManager: jest.fn().mockImplementation(() => ({
- getImportStateFromStorageByGroupId(groupId) {
- if (groupId === statusEndpointFixture.importable_data[TARGET_INDEX].id) {
- return {
- jobId: FAKE_JOB_ID,
- importState: {
- status: FAKE_STATUS,
- importTarget: FAKE_IMPORT_TARGET,
- },
- };
- }
-
- return null;
- },
- })),
- });
-
- const clientResponse = await clientWithMockedManager.query({
+ const updatedResults = await client.query({
query: bulkImportSourceGroupsQuery,
+ fetchPolicy: 'no-cache',
});
- const clientResults = clientResponse.data.bulkImportSourceGroups.nodes;
-
- expect(clientResults[TARGET_INDEX].import_target).toStrictEqual(FAKE_IMPORT_TARGET);
- expect(clientResults[TARGET_INDEX].progress.status).toBe(FAKE_STATUS);
- });
-
- it('populates each result instance with empty import_target when there are no available namespaces', async () => {
- axiosMockAdapter.onGet(FAKE_ENDPOINTS.availableNamespaces).reply(httpStatus.OK, []);
-
- const response = await client.query({ query: bulkImportSourceGroupsQuery });
- results = response.data.bulkImportSourceGroups.nodes;
- expect(results.every((r) => r.import_target.target_namespace === '')).toBe(true);
+ expect(updatedResults.data.bulkImportSourceGroups.nodes[0].progress).toStrictEqual({
+ __typename: clientTypenames.BulkImportProgress,
+ ...CACHED_DATA.progress,
+ });
});
describe('when called', () => {
@@ -181,37 +120,23 @@ describe('Bulk import resolvers', () => {
});
it('mirrors REST endpoint response fields', () => {
- const MIRRORED_FIELDS = ['id', 'full_name', 'full_path', 'web_url'];
+ const MIRRORED_FIELDS = [
+ { from: 'id', to: 'id' },
+ { from: 'full_name', to: 'fullName' },
+ { from: 'full_path', to: 'fullPath' },
+ { from: 'web_url', to: 'webUrl' },
+ ];
expect(
results.every((r, idx) =>
MIRRORED_FIELDS.every(
- (field) => r[field] === statusEndpointFixture.importable_data[idx][field],
+ (field) => r[field.to] === statusEndpointFixture.importable_data[idx][field.from],
),
),
).toBe(true);
});
- it('populates each result instance with status default to none', () => {
- expect(results.every((r) => r.progress.status === STATUSES.NONE)).toBe(true);
- });
-
- it('populates each result instance with import_target defaulted to first available namespace', () => {
- expect(
- results.every(
- (r) => r.import_target.target_namespace === availableNamespacesFixture[0].full_path,
- ),
- ).toBe(true);
- });
-
- it('starts polling when request completes', async () => {
- 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('populates each result instance with empty status', () => {
+ expect(results.every((r) => r.progress === null)).toBe(true);
});
});
@@ -223,6 +148,7 @@ describe('Bulk import resolvers', () => {
`(
'properly passes GraphQL variable $variable as REST $queryParam query parameter',
async ({ variable, queryParam, value }) => {
+ axiosMockAdapter.resetHistory();
await client.query({
query: bulkImportSourceGroupsQuery,
variables: { [variable]: value },
@@ -237,275 +163,61 @@ describe('Bulk import resolvers', () => {
});
describe('mutations', () => {
- const GROUP_ID = 1;
-
beforeEach(() => {
- client.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: {
- bulkImportSourceGroups: {
- nodes: [
- {
- __typename: clientTypenames.BulkImportSourceGroup,
- id: GROUP_ID,
- progress: {
- id: `test-${GROUP_ID}`,
- status: STATUSES.NONE,
- },
- web_url: 'https://fake.host/1',
- full_path: 'fake_group_1',
- full_name: 'fake_name_1',
- import_target: {
- target_namespace: 'root',
- new_name: 'group1',
- },
- last_import_target: {
- target_namespace: 'root',
- new_name: 'group1',
- },
- validation_errors: [],
- },
- ],
- pageInfo: {
- page: 1,
- perPage: 20,
- total: 37,
- totalPages: 2,
- },
- },
- },
- });
+ axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK, { id: 1 });
});
- 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: 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);
- });
-
- it('invokes validation', async () => {
- const NEW_TARGET_NAMESPACE = 'target';
- const NEW_NAME = 'new';
-
+ describe('importGroup', () => {
+ it('sets import status to CREATED when request completes', async () => {
await client.mutate({
- mutation: setImportTargetMutation,
+ mutation: importGroupsMutation,
variables: {
- sourceGroupId: GROUP_ID,
- targetNamespace: NEW_TARGET_NAMESPACE,
- newName: NEW_NAME,
+ importRequests: [
+ {
+ sourceGroupId: statusEndpointFixture.importable_data[0].id,
+ newName: 'test',
+ targetNamespace: 'root',
+ },
+ ],
},
});
- expect(FAKE_GROUP_AND_PROJECTS_QUERY_HANDLER).toHaveBeenCalledWith({
- fullPath: `${NEW_TARGET_NAMESPACE}/${NEW_NAME}`,
- });
- });
- });
-
- describe('importGroup', () => {
- it('sets status to SCHEDULING when request initiates', async () => {
- axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(() => new Promise(() => {}));
-
- client.mutate({
- mutation: importGroupsMutation,
- variables: { sourceGroupIds: [GROUP_ID] },
- });
- await waitForPromises();
-
- const {
- bulkImportSourceGroups: { nodes: intermediateResults },
- } = client.readQuery({
- query: bulkImportSourceGroupsQuery,
- });
-
- expect(intermediateResults[0].progress.status).toBe(STATUSES.SCHEDULING);
- });
-
- describe('when request completes', () => {
- let results;
-
- beforeEach(() => {
- client
- .watchQuery({
- query: bulkImportSourceGroupsQuery,
- fetchPolicy: 'cache-only',
- })
- .subscribe(({ data }) => {
- results = data.bulkImportSourceGroups.nodes;
- });
- });
-
- it('sets import status to CREATED when request completes', async () => {
- axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK, { id: 1 });
- await client.mutate({
- mutation: importGroupsMutation,
- variables: { sourceGroupIds: [GROUP_ID] },
- });
- await waitForPromises();
-
- expect(results[0].progress.status).toBe(STATUSES.CREATED);
- });
-
- it('resets status to NONE if request fails', async () => {
- axiosMockAdapter
- .onPost(FAKE_ENDPOINTS.createBulkImport)
- .reply(httpStatus.INTERNAL_SERVER_ERROR);
-
- client
- .mutate({
- mutation: [importGroupsMutation],
- variables: { sourceGroupIds: [GROUP_ID] },
- })
- .catch(() => {});
- await waitForPromises();
-
- expect(results[0].progress.status).toBe(STATUSES.NONE);
- });
- });
-
- it('shows default error message when server error is not provided', async () => {
- axiosMockAdapter
- .onPost(FAKE_ENDPOINTS.createBulkImport)
- .reply(httpStatus.INTERNAL_SERVER_ERROR);
-
- client
- .mutate({
- mutation: importGroupsMutation,
- variables: { sourceGroupIds: [GROUP_ID] },
- })
- .catch(() => {});
- await waitForPromises();
-
- expect(createFlash).toHaveBeenCalledWith({ message: 'Importing the group failed' });
- });
-
- it('shows provided error message when error is included in backend response', async () => {
- const CUSTOM_MESSAGE = 'custom message';
-
- axiosMockAdapter
- .onPost(FAKE_ENDPOINTS.createBulkImport)
- .reply(httpStatus.INTERNAL_SERVER_ERROR, { error: CUSTOM_MESSAGE });
-
- client
- .mutate({
- mutation: importGroupsMutation,
- variables: { sourceGroupIds: [GROUP_ID] },
- })
- .catch(() => {});
- await waitForPromises();
-
- expect(createFlash).toHaveBeenCalledWith({ message: CUSTOM_MESSAGE });
+ await axios.waitForAll();
+ expect(results[0].progress.status).toBe(STATUSES.CREATED);
});
});
- it('setImportProgress updates group progress and sets import target', async () => {
+ it('updateImportStatus updates status', async () => {
const NEW_STATUS = 'dummy';
- const FAKE_JOB_ID = 5;
- const IMPORT_TARGET = {
- __typename: 'ClientBulkImportTarget',
- new_name: 'fake_name',
- target_namespace: 'fake_target',
- };
- const {
- data: {
- setImportProgress: { progress, last_import_target: lastImportTarget },
- },
- } = await client.mutate({
- mutation: setImportProgressMutation,
+ await client.mutate({
+ mutation: importGroupsMutation,
variables: {
- sourceGroupId: GROUP_ID,
- status: NEW_STATUS,
- jobId: FAKE_JOB_ID,
- importTarget: IMPORT_TARGET,
+ importRequests: [
+ {
+ sourceGroupId: statusEndpointFixture.importable_data[0].id,
+ newName: 'test',
+ targetNamespace: 'root',
+ },
+ ],
},
});
+ await axios.waitForAll();
+ await waitForPromises();
- expect(lastImportTarget).toStrictEqual(IMPORT_TARGET);
-
- expect(progress).toStrictEqual({
- __typename: clientTypenames.BulkImportProgress,
- id: FAKE_JOB_ID,
- status: NEW_STATUS,
- });
- });
+ const { id } = results[0].progress;
- it('updateImportStatus returns new status', async () => {
- const NEW_STATUS = 'dummy';
- const FAKE_JOB_ID = 5;
const {
data: { updateImportStatus: statusInResponse },
} = await client.mutate({
mutation: updateImportStatusMutation,
- variables: { id: FAKE_JOB_ID, status: NEW_STATUS },
+ variables: { id, status: NEW_STATUS },
});
expect(statusInResponse).toStrictEqual({
__typename: clientTypenames.BulkImportProgress,
- id: FAKE_JOB_ID,
+ id,
status: NEW_STATUS,
});
});
-
- it('addValidationError adds error to group', async () => {
- const FAKE_FIELD = 'some-field';
- const FAKE_MESSAGE = 'some-message';
- const {
- data: {
- addValidationError: { validation_errors: validationErrors },
- },
- } = await client.mutate({
- mutation: addValidationErrorMutation,
- variables: { sourceGroupId: GROUP_ID, field: FAKE_FIELD, message: FAKE_MESSAGE },
- });
-
- expect(validationErrors).toStrictEqual([
- {
- __typename: clientTypenames.BulkImportValidationError,
- field: FAKE_FIELD,
- message: FAKE_MESSAGE,
- },
- ]);
- });
-
- it('removeValidationError removes error from group', async () => {
- const FAKE_FIELD = 'some-field';
- const FAKE_MESSAGE = 'some-message';
-
- await client.mutate({
- mutation: addValidationErrorMutation,
- variables: { sourceGroupId: GROUP_ID, field: FAKE_FIELD, message: FAKE_MESSAGE },
- });
-
- const {
- data: {
- removeValidationError: { validation_errors: validationErrors },
- },
- } = await client.mutate({
- mutation: removeValidationErrorMutation,
- variables: { sourceGroupId: GROUP_ID, field: FAKE_FIELD },
- });
-
- expect(validationErrors).toStrictEqual([]);
- });
});
});
diff --git a/spec/frontend/import_entities/import_groups/graphql/fixtures.js b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
index d1bd52693b6..5f6f9987a8f 100644
--- a/spec/frontend/import_entities/import_groups/graphql/fixtures.js
+++ b/spec/frontend/import_entities/import_groups/graphql/fixtures.js
@@ -1,24 +1,24 @@
+import { STATUSES } from '~/import_entities/constants';
import { clientTypenames } from '~/import_entities/import_groups/graphql/client_factory';
export const generateFakeEntry = ({ id, status, ...rest }) => ({
__typename: clientTypenames.BulkImportSourceGroup,
- web_url: `https://fake.host/${id}`,
- full_path: `fake_group_${id}`,
- full_name: `fake_name_${id}`,
- import_target: {
- target_namespace: 'root',
- new_name: `group${id}`,
- },
- last_import_target: {
- target_namespace: 'root',
- new_name: `last-group${id}`,
+ webUrl: `https://fake.host/${id}`,
+ fullPath: `fake_group_${id}`,
+ fullName: `fake_name_${id}`,
+ lastImportTarget: {
+ id,
+ targetNamespace: 'root',
+ newName: `group${id}`,
},
id,
- progress: {
- id: `test-${id}`,
- status,
- },
- validation_errors: [],
+ progress:
+ status === STATUSES.NONE || status === STATUSES.PENDING
+ ? null
+ : {
+ id,
+ status,
+ },
...rest,
});
@@ -51,9 +51,9 @@ export const statusEndpointFixture = {
],
};
-export const availableNamespacesFixture = [
- { id: 24, full_path: 'Commit451' },
- { id: 22, full_path: 'gitlab-org' },
- { id: 23, full_path: 'gnuwget' },
- { id: 25, full_path: 'jashkenas' },
-];
+export const availableNamespacesFixture = Object.freeze([
+ { id: 24, fullPath: 'Commit451' },
+ { id: 22, fullPath: 'gitlab-org' },
+ { id: 23, fullPath: 'gnuwget' },
+ { id: 25, fullPath: 'jashkenas' },
+]);
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/local_storage_cache_spec.js b/spec/frontend/import_entities/import_groups/graphql/services/local_storage_cache_spec.js
new file mode 100644
index 00000000000..b44a2767ad8
--- /dev/null
+++ b/spec/frontend/import_entities/import_groups/graphql/services/local_storage_cache_spec.js
@@ -0,0 +1,61 @@
+import {
+ KEY,
+ LocalStorageCache,
+} from '~/import_entities/import_groups/graphql/services/local_storage_cache';
+
+describe('Local storage cache', () => {
+ let cache;
+ let storage;
+
+ beforeEach(() => {
+ storage = {
+ getItem: jest.fn(),
+ setItem: jest.fn(),
+ };
+
+ cache = new LocalStorageCache({ storage });
+ });
+
+ describe('storage management', () => {
+ const IMPORT_URL = 'http://fake.url';
+
+ it('loads state from storage on creation', () => {
+ expect(storage.getItem).toHaveBeenCalledWith(KEY);
+ });
+
+ it('saves to storage when set is called', () => {
+ const STORAGE_CONTENT = { fake: 'content ' };
+ cache.set(IMPORT_URL, STORAGE_CONTENT);
+ expect(storage.setItem).toHaveBeenCalledWith(
+ KEY,
+ JSON.stringify({ [IMPORT_URL]: STORAGE_CONTENT }),
+ );
+ });
+
+ it('updates status by job id', () => {
+ const CHANGED_STATUS = 'changed';
+ const JOB_ID = 2;
+
+ cache.set(IMPORT_URL, {
+ progress: {
+ id: JOB_ID,
+ status: 'original',
+ },
+ });
+
+ cache.updateStatusByJobId(JOB_ID, CHANGED_STATUS);
+
+ expect(storage.setItem).toHaveBeenCalledWith(
+ KEY,
+ JSON.stringify({
+ [IMPORT_URL]: {
+ progress: {
+ id: JOB_ID,
+ status: CHANGED_STATUS,
+ },
+ },
+ }),
+ );
+ });
+ });
+});
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js b/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js
deleted file mode 100644
index f06babcb149..00000000000
--- a/spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import {
- KEY,
- SourceGroupsManager,
-} from '~/import_entities/import_groups/graphql/services/source_groups_manager';
-
-const FAKE_SOURCE_URL = 'http://demo.host';
-
-describe('SourceGroupsManager', () => {
- let manager;
- let storage;
-
- beforeEach(() => {
- storage = {
- getItem: jest.fn(),
- setItem: jest.fn(),
- };
-
- manager = new SourceGroupsManager({ storage, sourceUrl: FAKE_SOURCE_URL });
- });
-
- describe('storage management', () => {
- const IMPORT_ID = 1;
- const IMPORT_TARGET = { new_name: 'demo', target_namespace: 'foo' };
- const STATUS = 'FAKE_STATUS';
- const FAKE_GROUP = { id: 1, import_target: IMPORT_TARGET, status: STATUS };
-
- it('loads state from storage on creation', () => {
- expect(storage.getItem).toHaveBeenCalledWith(KEY);
- });
-
- it('saves to storage when createImportState is called', () => {
- const FAKE_STATUS = 'fake;';
- manager.createImportState(IMPORT_ID, { status: FAKE_STATUS, groups: [FAKE_GROUP] });
- const storedObject = JSON.parse(storage.setItem.mock.calls[0][1]);
- expect(Object.values(storedObject)[0]).toStrictEqual({
- status: FAKE_STATUS,
- groups: [
- {
- id: FAKE_GROUP.id,
- importTarget: IMPORT_TARGET,
- },
- ],
- });
- });
-
- it('updates storage when previous state is available', () => {
- const CHANGED_STATUS = 'changed';
-
- manager.createImportState(IMPORT_ID, { status: STATUS, groups: [FAKE_GROUP] });
-
- manager.updateImportProgress(IMPORT_ID, CHANGED_STATUS);
- const storedObject = JSON.parse(storage.setItem.mock.calls[1][1]);
- expect(Object.values(storedObject)[0]).toStrictEqual({
- status: CHANGED_STATUS,
- groups: [
- {
- id: FAKE_GROUP.id,
- importTarget: IMPORT_TARGET,
- },
- ],
- });
- });
- });
-});
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js b/spec/frontend/import_entities/import_groups/services/status_poller_spec.js
index 9c47647c430..01f976562c6 100644
--- a/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
+++ b/spec/frontend/import_entities/import_groups/services/status_poller_spec.js
@@ -2,19 +2,13 @@ import MockAdapter from 'axios-mock-adapter';
import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import { STATUSES } from '~/import_entities/constants';
-import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
+import { StatusPoller } from '~/import_entities/import_groups/services/status_poller';
import axios from '~/lib/utils/axios_utils';
import Poll from '~/lib/utils/poll';
jest.mock('visibilityjs');
jest.mock('~/flash');
jest.mock('~/lib/utils/poll');
-jest.mock('~/import_entities/import_groups/graphql/services/source_groups_manager', () => ({
- SourceGroupsManager: jest.fn().mockImplementation(function mock() {
- this.setImportStatus = jest.fn();
- this.findByImportId = jest.fn();
- }),
-}));
const FAKE_POLL_PATH = '/fake/poll/path';
@@ -81,6 +75,7 @@ describe('Bulk import status poller', () => {
const [pollInstance] = Poll.mock.instances;
poller.startPolling();
+ await Promise.resolve();
expect(pollInstance.makeRequest).toHaveBeenCalled();
});