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/graphql')
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/client_factory_spec.js137
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/source_groups_manager_spec.js4
-rw-r--r--spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js276
3 files changed, 172 insertions, 245 deletions
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 514ed411138..4d3d2c41bbe 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
@@ -1,20 +1,20 @@
-import MockAdapter from 'axios-mock-adapter';
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 axios from '~/lib/utils/axios_utils';
+import { STATUSES } from '~/import_entities/constants';
import {
clientTypenames,
createResolvers,
} from '~/import_entities/import_groups/graphql/client_factory';
+import importGroupMutation from '~/import_entities/import_groups/graphql/mutations/import_group.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 availableNamespacesQuery from '~/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql';
+import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
-import { STATUSES } from '~/import_entities/constants';
-import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
-import availableNamespacesQuery from '~/import_entities/import_groups/graphql/queries/available_namespaces.query.graphql';
-import setTargetNamespaceMutation from '~/import_entities/import_groups/graphql/mutations/set_target_namespace.mutation.graphql';
-import setNewNameMutation from '~/import_entities/import_groups/graphql/mutations/set_new_name.mutation.graphql';
-import importGroupMutation from '~/import_entities/import_groups/graphql/mutations/import_group.mutation.graphql';
+import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { statusEndpointFixture, availableNamespacesFixture } from './fixtures';
@@ -28,6 +28,7 @@ const FAKE_ENDPOINTS = {
status: '/fake_status_url',
availableNamespaces: '/fake_available_namespaces',
createBulkImport: '/fake_create_bulk_import',
+ jobs: '/fake_jobs',
};
describe('Bulk import resolvers', () => {
@@ -79,33 +80,61 @@ describe('Bulk import resolvers', () => {
axiosMockAdapter
.onGet(FAKE_ENDPOINTS.availableNamespaces)
.reply(httpStatus.OK, availableNamespacesFixture);
-
- const response = await client.query({ query: bulkImportSourceGroupsQuery });
- results = response.data.bulkImportSourceGroups;
});
- it('mirrors REST endpoint response fields', () => {
- const MIRRORED_FIELDS = ['id', 'full_name', 'full_path', 'web_url'];
- expect(
- results.every((r, idx) =>
- MIRRORED_FIELDS.every(
- (field) => r[field] === statusEndpointFixture.importable_data[idx][field],
+ describe('when called', () => {
+ beforeEach(async () => {
+ const response = await client.query({ query: bulkImportSourceGroupsQuery });
+ results = response.data.bulkImportSourceGroups.nodes;
+ });
+
+ it('mirrors REST endpoint response fields', () => {
+ const MIRRORED_FIELDS = ['id', 'full_name', 'full_path', 'web_url'];
+ expect(
+ results.every((r, idx) =>
+ MIRRORED_FIELDS.every(
+ (field) => r[field] === statusEndpointFixture.importable_data[idx][field],
+ ),
),
- ),
- ).toBe(true);
- });
+ ).toBe(true);
+ });
- it('populates each result instance with status field default to none', () => {
- expect(results.every((r) => r.status === STATUSES.NONE)).toBe(true);
- });
+ it('populates each result instance with status field default to none', () => {
+ expect(results.every((r) => r.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('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.each`
+ variable | queryParam | value
+ ${'filter'} | ${'filter'} | ${'demo'}
+ ${'perPage'} | ${'per_page'} | ${30}
+ ${'page'} | ${'page'} | ${3}
+ `(
+ 'properly passes GraphQL variable $variable as REST $queryParam query parameter',
+ async ({ variable, queryParam, value }) => {
+ await client.query({
+ query: bulkImportSourceGroupsQuery,
+ variables: { [variable]: value },
+ });
+ const restCall = axiosMockAdapter.history.get.find(
+ (q) => q.url === FAKE_ENDPOINTS.status,
+ );
+ expect(restCall.params[queryParam]).toBe(value);
+ },
+ );
});
});
@@ -117,20 +146,28 @@ describe('Bulk import resolvers', () => {
client.writeQuery({
query: bulkImportSourceGroupsQuery,
data: {
- bulkImportSourceGroups: [
- {
- __typename: clientTypenames.BulkImportSourceGroup,
- id: 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',
+ bulkImportSourceGroups: {
+ nodes: [
+ {
+ __typename: clientTypenames.BulkImportSourceGroup,
+ id: 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',
+ },
},
+ ],
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ total: 37,
+ totalPages: 2,
},
- ],
+ },
},
});
@@ -140,7 +177,7 @@ describe('Bulk import resolvers', () => {
fetchPolicy: 'cache-only',
})
.subscribe(({ data }) => {
- results = data.bulkImportSourceGroups;
+ results = data.bulkImportSourceGroups.nodes;
});
});
@@ -174,7 +211,9 @@ describe('Bulk import resolvers', () => {
});
await waitForPromises();
- const { bulkImportSourceGroups: intermediateResults } = client.readQuery({
+ const {
+ bulkImportSourceGroups: { nodes: intermediateResults },
+ } = client.readQuery({
query: bulkImportSourceGroupsQuery,
});
@@ -182,7 +221,7 @@ describe('Bulk import resolvers', () => {
});
it('sets group status to STARTED when request completes', async () => {
- axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK);
+ axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK, { id: 1 });
await client.mutate({
mutation: importGroupMutation,
variables: { sourceGroupId: GROUP_ID },
@@ -191,16 +230,6 @@ describe('Bulk import resolvers', () => {
expect(results[0].status).toBe(STATUSES.STARTED);
});
- it('starts polling when request completes', async () => {
- axiosMockAdapter.onPost(FAKE_ENDPOINTS.createBulkImport).reply(httpStatus.OK);
- await client.mutate({
- mutation: importGroupMutation,
- variables: { sourceGroupId: GROUP_ID },
- });
- const [statusPoller] = StatusPoller.mock.instances;
- expect(statusPoller.startPolling).toHaveBeenCalled();
- });
-
it('resets status to NONE if request fails', async () => {
axiosMockAdapter
.onPost(FAKE_ENDPOINTS.createBulkImport)
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
index 5940ea544ea..ca987ab3ab4 100644
--- 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
@@ -1,7 +1,7 @@
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
-import { SourceGroupsManager } from '~/import_entities/import_groups/graphql/services/source_groups_manager';
-import ImportSourceGroupFragment from '~/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql';
import { clientTypenames } from '~/import_entities/import_groups/graphql/client_factory';
+import ImportSourceGroupFragment from '~/import_entities/import_groups/graphql/fragments/bulk_import_source_group_item.fragment.graphql';
+import { SourceGroupsManager } from '~/import_entities/import_groups/graphql/services/source_groups_manager';
describe('SourceGroupsManager', () => {
let manager;
diff --git a/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js b/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
index e7f1626f81d..a5fc4e18a02 100644
--- a/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
+++ b/spec/frontend/import_entities/import_groups/graphql/services/status_poller_spec.js
@@ -1,215 +1,113 @@
-import { createMockClient } from 'mock-apollo-client';
-import { InMemoryCache } from 'apollo-cache-inmemory';
-import waitForPromises from 'helpers/wait_for_promises';
-
+import MockAdapter from 'axios-mock-adapter';
+import Visibility from 'visibilityjs';
import createFlash from '~/flash';
-import { StatusPoller } from '~/import_entities/import_groups/graphql/services/status_poller';
-import bulkImportSourceGroupsQuery from '~/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql';
import { STATUSES } from '~/import_entities/constants';
import { SourceGroupsManager } from '~/import_entities/import_groups/graphql/services/source_groups_manager';
-import { generateFakeEntry } from '../fixtures';
+import { StatusPoller } from '~/import_entities/import_groups/graphql/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 TEST_POLL_INTERVAL = 1000;
+const FAKE_POLL_PATH = '/fake/poll/path';
+const CLIENT_MOCK = {};
describe('Bulk import status poller', () => {
let poller;
- let clientMock;
+ let mockAdapter;
- const listQueryCacheCalls = () =>
- clientMock.readQuery.mock.calls.filter((call) => call[0].query === bulkImportSourceGroupsQuery);
+ const getPollHistory = () => mockAdapter.history.get.filter((x) => x.url === FAKE_POLL_PATH);
beforeEach(() => {
- clientMock = createMockClient({
- cache: new InMemoryCache({
- fragmentMatcher: { match: () => true },
- }),
- });
-
- jest.spyOn(clientMock, 'readQuery');
-
- poller = new StatusPoller({
- client: clientMock,
- interval: TEST_POLL_INTERVAL,
- });
+ mockAdapter = new MockAdapter(axios);
+ mockAdapter.onGet(FAKE_POLL_PATH).reply(200, {});
+ poller = new StatusPoller({ client: CLIENT_MOCK, pollPath: FAKE_POLL_PATH });
});
- describe('general behavior', () => {
- beforeEach(() => {
- clientMock.cache.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: { bulkImportSourceGroups: [] },
- });
- });
-
- it('does not perform polling when constructed', () => {
- jest.runOnlyPendingTimers();
- expect(listQueryCacheCalls()).toHaveLength(0);
- });
-
- it('immediately start polling when requested', async () => {
- await poller.startPolling();
- expect(listQueryCacheCalls()).toHaveLength(1);
- });
-
- it('constantly polls when started', async () => {
- poller.startPolling();
- expect(listQueryCacheCalls()).toHaveLength(1);
-
- jest.advanceTimersByTime(TEST_POLL_INTERVAL);
- expect(listQueryCacheCalls()).toHaveLength(2);
-
- jest.advanceTimersByTime(TEST_POLL_INTERVAL);
- expect(listQueryCacheCalls()).toHaveLength(3);
- });
-
- it('does not start polling when requested multiple times', async () => {
- poller.startPolling();
- expect(listQueryCacheCalls()).toHaveLength(1);
-
- poller.startPolling();
- expect(listQueryCacheCalls()).toHaveLength(1);
- });
-
- it('stops polling when requested', async () => {
- poller.startPolling();
- expect(listQueryCacheCalls()).toHaveLength(1);
-
- poller.stopPolling();
- jest.runOnlyPendingTimers();
- expect(listQueryCacheCalls()).toHaveLength(1);
- });
-
- it('does not query server when list is empty', async () => {
- jest.spyOn(clientMock, 'query');
- poller.startPolling();
- expect(clientMock.query).not.toHaveBeenCalled();
- });
+ it('creates source group manager with proper client', () => {
+ expect(SourceGroupsManager.mock.calls).toHaveLength(1);
+ const [[{ client }]] = SourceGroupsManager.mock.calls;
+ expect(client).toBe(CLIENT_MOCK);
});
- it('does not query server when no groups have STARTED status', async () => {
- clientMock.cache.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: {
- bulkImportSourceGroups: [STATUSES.NONE, STATUSES.FINISHED].map((status, idx) =>
- generateFakeEntry({ status, id: idx }),
- ),
- },
- });
-
- jest.spyOn(clientMock, 'query');
+ it('creates poller with proper config', () => {
+ expect(Poll.mock.calls).toHaveLength(1);
+ const [[pollConfig]] = Poll.mock.calls;
+ expect(typeof pollConfig.method).toBe('string');
+
+ const pollOperation = pollConfig.resource[pollConfig.method];
+ expect(typeof pollOperation).toBe('function');
+ });
+
+ it('invokes axios when polling is performed', async () => {
+ const [[pollConfig]] = Poll.mock.calls;
+ const pollOperation = pollConfig.resource[pollConfig.method];
+ expect(getPollHistory()).toHaveLength(0);
+
+ pollOperation();
+ await axios.waitForAll();
+
+ expect(getPollHistory()).toHaveLength(1);
+ });
+
+ it('subscribes to visibility changes', () => {
+ expect(Visibility.change).toHaveBeenCalled();
+ });
+
+ it.each`
+ isHidden | action
+ ${true} | ${'stop'}
+ ${false} | ${'restart'}
+ `('$action polling when hidden is $isHidden', ({ action, isHidden }) => {
+ const [pollInstance] = Poll.mock.instances;
+ const [[changeHandler]] = Visibility.change.mock.calls;
+ Visibility.hidden.mockReturnValue(isHidden);
+ expect(pollInstance[action]).not.toHaveBeenCalled();
+
+ changeHandler();
+
+ expect(pollInstance[action]).toHaveBeenCalled();
+ });
+
+ it('does not perform polling when constructed', async () => {
+ await axios.waitForAll();
+
+ expect(getPollHistory()).toHaveLength(0);
+ });
+
+ it('immediately start polling when requested', async () => {
+ const [pollInstance] = Poll.mock.instances;
+
poller.startPolling();
- expect(clientMock.query).not.toHaveBeenCalled();
+
+ expect(pollInstance.makeRequest).toHaveBeenCalled();
+ });
+
+ it('when error occurs shows flash with error', () => {
+ const [[pollConfig]] = Poll.mock.calls;
+ pollConfig.errorCallback();
+ expect(createFlash).toHaveBeenCalled();
});
- describe('when there are groups which have STARTED status', () => {
- const TARGET_NAMESPACE = 'root';
-
- const STARTED_GROUP_1 = {
- status: STATUSES.STARTED,
- id: 'started1',
- import_target: {
- target_namespace: TARGET_NAMESPACE,
- new_name: 'group1',
- },
- };
-
- const STARTED_GROUP_2 = {
- status: STATUSES.STARTED,
- id: 'started2',
- import_target: {
- target_namespace: TARGET_NAMESPACE,
- new_name: 'group2',
- },
- };
-
- const NOT_STARTED_GROUP = {
- status: STATUSES.NONE,
- id: 'not_started',
- import_target: {
- target_namespace: TARGET_NAMESPACE,
- new_name: 'group3',
- },
- };
-
- it('query server only for groups with STATUSES.STARTED', async () => {
- clientMock.cache.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: {
- bulkImportSourceGroups: [
- STARTED_GROUP_1,
- NOT_STARTED_GROUP,
- STARTED_GROUP_2,
- ].map((group) => generateFakeEntry(group)),
- },
- });
-
- clientMock.query = jest.fn().mockResolvedValue({ data: {} });
- poller.startPolling();
-
- expect(clientMock.query).toHaveBeenCalledTimes(1);
- await waitForPromises();
- const [[doc]] = clientMock.query.mock.calls;
- const { selections } = doc.query.definitions[0].selectionSet;
- expect(selections.every((field) => field.name.value === 'group')).toBeTruthy();
- expect(selections).toHaveLength(2);
- expect(selections.map((sel) => sel.arguments[0].value.value)).toStrictEqual([
- `${TARGET_NAMESPACE}/${STARTED_GROUP_1.import_target.new_name}`,
- `${TARGET_NAMESPACE}/${STARTED_GROUP_2.import_target.new_name}`,
- ]);
- });
-
- it('updates statuses only for groups in response', async () => {
- clientMock.cache.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: {
- bulkImportSourceGroups: [STARTED_GROUP_1, STARTED_GROUP_2].map((group) =>
- generateFakeEntry(group),
- ),
- },
- });
-
- clientMock.query = jest.fn().mockResolvedValue({ data: { group0: {} } });
- poller.startPolling();
- await waitForPromises();
- const [managerInstance] = SourceGroupsManager.mock.instances;
- expect(managerInstance.setImportStatus).toHaveBeenCalledTimes(1);
- expect(managerInstance.setImportStatus).toHaveBeenCalledWith(
- expect.objectContaining({ id: STARTED_GROUP_1.id }),
- STATUSES.FINISHED,
- );
- });
-
- describe('when error occurs', () => {
- beforeEach(() => {
- clientMock.cache.writeQuery({
- query: bulkImportSourceGroupsQuery,
- data: {
- bulkImportSourceGroups: [STARTED_GROUP_1, STARTED_GROUP_2].map((group) =>
- generateFakeEntry(group),
- ),
- },
- });
-
- clientMock.query = jest.fn().mockRejectedValue(new Error('dummy error'));
- poller.startPolling();
- return waitForPromises();
- });
-
- it('reports an error', () => {
- expect(createFlash).toHaveBeenCalled();
- });
-
- it('continues polling', async () => {
- jest.advanceTimersByTime(TEST_POLL_INTERVAL);
- expect(listQueryCacheCalls()).toHaveLength(2);
- });
- });
+ it('when success response arrives updates relevant group status', () => {
+ const FAKE_ID = 5;
+ const [[pollConfig]] = Poll.mock.calls;
+ const [managerInstance] = SourceGroupsManager.mock.instances;
+ managerInstance.findByImportId.mockReturnValue({ id: FAKE_ID });
+
+ pollConfig.successCallback({ data: [{ id: FAKE_ID, status_name: STATUSES.FINISHED }] });
+
+ expect(managerInstance.setImportStatus).toHaveBeenCalledWith(
+ expect.objectContaining({ id: FAKE_ID }),
+ STATUSES.FINISHED,
+ );
});
});