summaryrefslogtreecommitdiff
path: root/spec/frontend/import_projects
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/import_projects')
-rw-r--r--spec/frontend/import_projects/components/import_projects_table_spec.js108
-rw-r--r--spec/frontend/import_projects/components/imported_project_table_row_spec.js65
-rw-r--r--spec/frontend/import_projects/components/page_query_param_sync_spec.js87
-rw-r--r--spec/frontend/import_projects/components/provider_repo_table_row_spec.js120
-rw-r--r--spec/frontend/import_projects/store/actions_spec.js221
-rw-r--r--spec/frontend/import_projects/store/getters_spec.js140
-rw-r--r--spec/frontend/import_projects/store/mutations_spec.js299
-rw-r--r--spec/frontend/import_projects/utils_spec.js32
8 files changed, 809 insertions, 263 deletions
diff --git a/spec/frontend/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_projects/components/import_projects_table_spec.js
index 419d67e239f..b217242968a 100644
--- a/spec/frontend/import_projects/components/import_projects_table_spec.js
+++ b/spec/frontend/import_projects/components/import_projects_table_spec.js
@@ -2,16 +2,14 @@ import { nextTick } from 'vue';
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlLoadingIcon, GlButton } from '@gitlab/ui';
-import { state, getters } from '~/import_projects/store';
-import eventHub from '~/import_projects/event_hub';
+import state from '~/import_projects/store/state';
+import * as getters from '~/import_projects/store/getters';
+import { STATUSES } from '~/import_projects/constants';
import ImportProjectsTable from '~/import_projects/components/import_projects_table.vue';
import ImportedProjectTableRow from '~/import_projects/components/imported_project_table_row.vue';
import ProviderRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
import IncompatibleRepoTableRow from '~/import_projects/components/incompatible_repo_table_row.vue';
-
-jest.mock('~/import_projects/event_hub', () => ({
- $emit: jest.fn(),
-}));
+import PageQueryParamSync from '~/import_projects/components/page_query_param_sync.vue';
describe('ImportProjectsTable', () => {
let wrapper;
@@ -21,13 +19,6 @@ describe('ImportProjectsTable', () => {
const providerTitle = 'THE PROVIDER';
const providerRepo = { id: 10, sanitizedName: 'sanitizedName', fullName: 'fullName' };
- const importedProject = {
- id: 1,
- fullPath: 'fullPath',
- importStatus: 'started',
- providerLink: 'providerLink',
- importSource: 'importSource',
- };
const findImportAllButton = () =>
wrapper
@@ -35,11 +26,15 @@ describe('ImportProjectsTable', () => {
.filter(w => w.props().variant === 'success')
.at(0);
+ const importAllFn = jest.fn();
+ const setPageFn = jest.fn();
+
function createComponent({
state: initialState,
getters: customGetters,
slots,
filterable,
+ paginatable,
} = {}) {
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -52,11 +47,13 @@ describe('ImportProjectsTable', () => {
},
actions: {
fetchRepos: jest.fn(),
- fetchReposFiltered: jest.fn(),
fetchJobs: jest.fn(),
+ fetchNamespaces: jest.fn(),
+ importAll: importAllFn,
stopJobsPolling: jest.fn(),
clearJobsEtagPoll: jest.fn(),
setFilter: jest.fn(),
+ setPage: setPageFn,
},
});
@@ -66,6 +63,7 @@ describe('ImportProjectsTable', () => {
propsData: {
providerTitle,
filterable,
+ paginatable,
},
slots,
});
@@ -79,11 +77,13 @@ describe('ImportProjectsTable', () => {
});
it('renders a loading icon while repos are loading', () => {
- createComponent({
- state: {
- isLoadingRepos: true,
- },
- });
+ createComponent({ state: { isLoadingRepos: true } });
+
+ expect(wrapper.contains(GlLoadingIcon)).toBe(true);
+ });
+
+ it('renders a loading icon while namespaces are loading', () => {
+ createComponent({ state: { isLoadingNamespaces: true } });
expect(wrapper.contains(GlLoadingIcon)).toBe(true);
});
@@ -91,10 +91,16 @@ describe('ImportProjectsTable', () => {
it('renders a table with imported projects and provider repos', () => {
createComponent({
state: {
- importedProjects: [importedProject],
- providerRepos: [providerRepo],
- incompatibleRepos: [{ ...providerRepo, id: 11 }],
- namespaces: [{ path: 'path' }],
+ namespaces: [{ fullPath: 'path' }],
+ repositories: [
+ { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
+ { importSource: { id: 2 }, importedProject: {}, importStatus: STATUSES.FINISHED },
+ {
+ importSource: { id: 3, incompatible: true },
+ importedProject: {},
+ importStatus: STATUSES.NONE,
+ },
+ ],
},
});
@@ -133,13 +139,7 @@ describe('ImportProjectsTable', () => {
);
it('renders an empty state if there are no projects available', () => {
- createComponent({
- state: {
- importedProjects: [],
- providerRepos: [],
- incompatibleProjects: [],
- },
- });
+ createComponent({ state: { repositories: [] } });
expect(wrapper.contains(ProviderRepoTableRow)).toBe(false);
expect(wrapper.contains(ImportedProjectTableRow)).toBe(false);
@@ -147,37 +147,63 @@ describe('ImportProjectsTable', () => {
});
it('sends importAll event when import button is clicked', async () => {
- createComponent({
- state: {
- providerRepos: [providerRepo],
- },
- });
+ createComponent({ state: { providerRepos: [providerRepo] } });
findImportAllButton().vm.$emit('click');
await nextTick();
- expect(eventHub.$emit).toHaveBeenCalledWith('importAll');
+
+ expect(importAllFn).toHaveBeenCalled();
});
it('shows loading spinner when import is in progress', () => {
- createComponent({
- getters: {
- isImportingAnyRepo: () => true,
- },
- });
+ createComponent({ getters: { isImportingAnyRepo: () => true } });
expect(findImportAllButton().props().loading).toBe(true);
});
it('renders filtering input field by default', () => {
createComponent();
+
expect(findFilterField().exists()).toBe(true);
});
it('does not render filtering input field when filterable is false', () => {
createComponent({ filterable: false });
+
expect(findFilterField().exists()).toBe(false);
});
+ describe('when paginatable is set to true', () => {
+ const pageInfo = { page: 1 };
+
+ beforeEach(() => {
+ createComponent({
+ state: {
+ namespaces: [{ fullPath: 'path' }],
+ pageInfo,
+ repositories: [
+ { importSource: { id: 1 }, importedProject: null, importStatus: STATUSES.NONE },
+ ],
+ },
+ paginatable: true,
+ });
+ });
+
+ it('passes current page to page-query-param-sync component', () => {
+ expect(wrapper.find(PageQueryParamSync).props().page).toBe(pageInfo.page);
+ });
+
+ it('dispatches setPage when page-query-param-sync emits popstate', () => {
+ const NEW_PAGE = 2;
+ wrapper.find(PageQueryParamSync).vm.$emit('popstate', NEW_PAGE);
+
+ const { calls } = setPageFn.mock;
+
+ expect(calls).toHaveLength(1);
+ expect(calls[0][1]).toBe(NEW_PAGE);
+ });
+ });
+
it.each`
hasIncompatibleRepos | shouldRenderSlot | action
${false} | ${false} | ${'does not render'}
diff --git a/spec/frontend/import_projects/components/imported_project_table_row_spec.js b/spec/frontend/import_projects/components/imported_project_table_row_spec.js
index 700dd1e025a..8890c352826 100644
--- a/spec/frontend/import_projects/components/imported_project_table_row_spec.js
+++ b/spec/frontend/import_projects/components/imported_project_table_row_spec.js
@@ -1,57 +1,44 @@
-import Vuex from 'vuex';
-import { createLocalVue, mount } from '@vue/test-utils';
-import createStore from '~/import_projects/store';
-import importedProjectTableRow from '~/import_projects/components/imported_project_table_row.vue';
-import STATUS_MAP from '~/import_projects/constants';
+import { mount } from '@vue/test-utils';
+import ImportedProjectTableRow from '~/import_projects/components/imported_project_table_row.vue';
+import ImportStatus from '~/import_projects/components/import_status.vue';
+import { STATUSES } from '~/import_projects/constants';
describe('ImportedProjectTableRow', () => {
- let vm;
+ let wrapper;
const project = {
- id: 1,
- fullPath: 'fullPath',
- importStatus: 'finished',
- providerLink: 'providerLink',
- importSource: 'importSource',
+ importSource: {
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ },
+ importedProject: {
+ id: 1,
+ fullPath: 'fullPath',
+ importSource: 'importSource',
+ },
+ importStatus: STATUSES.FINISHED,
};
function mountComponent() {
- const localVue = createLocalVue();
- localVue.use(Vuex);
-
- const component = mount(importedProjectTableRow, {
- localVue,
- store: createStore(),
- propsData: {
- project: {
- ...project,
- },
- },
- });
-
- return component.vm;
+ wrapper = mount(ImportedProjectTableRow, { propsData: { project } });
}
beforeEach(() => {
- vm = mountComponent();
+ mountComponent();
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('renders an imported project table row', () => {
- const providerLink = vm.$el.querySelector('.js-provider-link');
- const statusObject = STATUS_MAP[project.importStatus];
-
- expect(vm.$el.classList.contains('js-imported-project')).toBe(true);
- expect(providerLink.href).toMatch(project.providerLink);
- expect(providerLink.textContent).toMatch(project.importSource);
- expect(vm.$el.querySelector('.js-full-path').textContent).toMatch(project.fullPath);
- expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
- statusObject.text,
+ const providerLink = wrapper.find('[data-testid=providerLink]');
+
+ expect(providerLink.attributes().href).toMatch(project.importSource.providerLink);
+ expect(providerLink.text()).toMatch(project.importSource.fullName);
+ expect(wrapper.find('[data-testid=fullPath]').text()).toMatch(project.importedProject.fullPath);
+ expect(wrapper.find(ImportStatus).props().status).toBe(project.importStatus);
+ expect(wrapper.find('[data-testid=goToProject').attributes().href).toMatch(
+ project.importedProject.fullPath,
);
-
- expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
- expect(vm.$el.querySelector('.js-go-to-project').href).toMatch(project.fullPath);
});
});
diff --git a/spec/frontend/import_projects/components/page_query_param_sync_spec.js b/spec/frontend/import_projects/components/page_query_param_sync_spec.js
new file mode 100644
index 00000000000..be19ecca1ba
--- /dev/null
+++ b/spec/frontend/import_projects/components/page_query_param_sync_spec.js
@@ -0,0 +1,87 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { TEST_HOST } from 'helpers/test_constants';
+
+import PageQueryParamSync from '~/import_projects/components/page_query_param_sync.vue';
+
+describe('PageQueryParamSync', () => {
+ let originalPushState;
+ let originalAddEventListener;
+ let originalRemoveEventListener;
+
+ const pushStateMock = jest.fn();
+ const addEventListenerMock = jest.fn();
+ const removeEventListenerMock = jest.fn();
+
+ beforeAll(() => {
+ window.location.search = '';
+ originalPushState = window.pushState;
+
+ window.history.pushState = pushStateMock;
+
+ originalAddEventListener = window.addEventListener;
+ window.addEventListener = addEventListenerMock;
+
+ originalRemoveEventListener = window.removeEventListener;
+ window.removeEventListener = removeEventListenerMock;
+ });
+
+ afterAll(() => {
+ window.history.pushState = originalPushState;
+ window.addEventListener = originalAddEventListener;
+ window.removeEventListener = originalRemoveEventListener;
+ });
+
+ let wrapper;
+ beforeEach(() => {
+ wrapper = shallowMount(PageQueryParamSync, {
+ propsData: { page: 3 },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('calls push state with page number when page is updated and differs from 1', async () => {
+ wrapper.setProps({ page: 2 });
+
+ await nextTick();
+
+ const { calls } = pushStateMock.mock;
+ expect(calls).toHaveLength(1);
+ expect(calls[0][2]).toBe(`${TEST_HOST}/?page=2`);
+ });
+
+ it('calls push state without page number when page is updated and is 1', async () => {
+ wrapper.setProps({ page: 1 });
+
+ await nextTick();
+
+ const { calls } = pushStateMock.mock;
+ expect(calls).toHaveLength(1);
+ expect(calls[0][2]).toBe(`${TEST_HOST}/`);
+ });
+
+ it('subscribes to popstate event on create', () => {
+ expect(addEventListenerMock).toHaveBeenCalledWith('popstate', expect.any(Function));
+ });
+
+ it('unsubscribes from popstate event when destroyed', () => {
+ const [, fn] = addEventListenerMock.mock.calls[0];
+
+ wrapper.destroy();
+
+ expect(removeEventListenerMock).toHaveBeenCalledWith('popstate', fn);
+ });
+
+ it('emits popstate event when popstate is triggered', async () => {
+ const [, fn] = addEventListenerMock.mock.calls[0];
+
+ delete window.location;
+ window.location = new URL(`${TEST_HOST}/?page=5`);
+ fn();
+
+ expect(wrapper.emitted().popstate[0]).toStrictEqual([5]);
+ });
+});
diff --git a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
index f5e5141eac8..bd9cd07db78 100644
--- a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
+++ b/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
@@ -1,100 +1,100 @@
+import { nextTick } from 'vue';
import Vuex from 'vuex';
-import { createLocalVue, mount } from '@vue/test-utils';
-import { state, actions, getters, mutations } from '~/import_projects/store';
-import providerRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
-import STATUS_MAP, { STATUSES } from '~/import_projects/constants';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import ProviderRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
+import ImportStatus from '~/import_projects/components/import_status.vue';
+import { STATUSES } from '~/import_projects/constants';
+import Select2Select from '~/vue_shared/components/select2_select.vue';
describe('ProviderRepoTableRow', () => {
- let vm;
+ let wrapper;
const fetchImport = jest.fn();
- const importPath = '/import-path';
- const defaultTargetNamespace = 'user';
- const ciCdOnly = true;
+ const setImportTarget = jest.fn();
+ const fakeImportTarget = {
+ targetNamespace: 'target',
+ newName: 'newName',
+ };
+ const ciCdOnly = false;
const repo = {
- id: 10,
- sanitizedName: 'sanitizedName',
- fullName: 'fullName',
- providerLink: 'providerLink',
+ importSource: {
+ id: 'remote-1',
+ fullName: 'fullName',
+ providerLink: 'providerLink',
+ },
+ importedProject: {
+ id: 1,
+ fullPath: 'fullPath',
+ importSource: 'importSource',
+ },
+ importStatus: STATUSES.FINISHED,
};
- function initStore(initialState) {
- const stubbedActions = { ...actions, fetchImport };
+ const availableNamespaces = [
+ { text: 'Groups', children: [{ id: 'test', text: 'test' }] },
+ { text: 'Users', children: [{ id: 'root', text: 'root' }] },
+ ];
+ function initStore(initialState) {
const store = new Vuex.Store({
- state: { ...state(), ...initialState },
- actions: stubbedActions,
- mutations,
- getters,
+ state: initialState,
+ getters: {
+ getImportTarget: () => () => fakeImportTarget,
+ },
+ actions: { fetchImport, setImportTarget },
});
return store;
}
+ const findImportButton = () =>
+ wrapper
+ .findAll('button')
+ .filter(node => node.text() === 'Import')
+ .at(0);
+
function mountComponent(initialState) {
const localVue = createLocalVue();
localVue.use(Vuex);
- const store = initStore({ importPath, defaultTargetNamespace, ciCdOnly, ...initialState });
+ const store = initStore({ ciCdOnly, ...initialState });
- const component = mount(providerRepoTableRow, {
+ wrapper = shallowMount(ProviderRepoTableRow, {
localVue,
store,
- propsData: {
- repo,
- },
+ propsData: { repo, availableNamespaces },
});
-
- return component.vm;
}
beforeEach(() => {
- vm = mountComponent();
+ mountComponent();
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
it('renders a provider repo table row', () => {
- const providerLink = vm.$el.querySelector('.js-provider-link');
- const statusObject = STATUS_MAP[STATUSES.NONE];
-
- expect(vm.$el.classList.contains('js-provider-repo')).toBe(true);
- expect(providerLink.href).toMatch(repo.providerLink);
- expect(providerLink.textContent).toMatch(repo.fullName);
- expect(vm.$el.querySelector(`.${statusObject.textClass}`).textContent).toMatch(
- statusObject.text,
- );
-
- expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
- expect(vm.$el.querySelector('.js-import-button')).not.toBeNull();
+ const providerLink = wrapper.find('[data-testid=providerLink]');
+
+ expect(providerLink.attributes().href).toMatch(repo.importSource.providerLink);
+ expect(providerLink.text()).toMatch(repo.importSource.fullName);
+ expect(wrapper.find(ImportStatus).props().status).toBe(repo.importStatus);
+ expect(wrapper.contains('button')).toBe(true);
});
it('renders a select2 namespace select', () => {
- const dropdownTrigger = vm.$el.querySelector('.js-namespace-select');
-
- expect(dropdownTrigger).not.toBeNull();
- expect(dropdownTrigger.classList.contains('select2-container')).toBe(true);
-
- dropdownTrigger.click();
-
- expect(vm.$el.querySelector('.select2-drop')).not.toBeNull();
+ expect(wrapper.contains(Select2Select)).toBe(true);
+ expect(wrapper.find(Select2Select).props().options.data).toBe(availableNamespaces);
});
- it('imports repo when clicking import button', () => {
- vm.$el.querySelector('.js-import-button').click();
+ it('imports repo when clicking import button', async () => {
+ findImportButton().trigger('click');
- return vm.$nextTick().then(() => {
- const { calls } = fetchImport.mock;
+ await nextTick();
- // Not using .toBeCalledWith because it expects
- // an unmatchable and undefined 3rd argument.
- expect(calls.length).toBe(1);
- expect(calls[0][1]).toEqual({
- repo,
- newName: repo.sanitizedName,
- targetNamespace: defaultTargetNamespace,
- });
- });
+ const { calls } = fetchImport.mock;
+
+ expect(calls).toHaveLength(1);
+ expect(calls[0][1]).toBe(repo.importSource.id);
});
});
diff --git a/spec/frontend/import_projects/store/actions_spec.js b/spec/frontend/import_projects/store/actions_spec.js
index fd6fbcbfce0..45a59b3f6d6 100644
--- a/spec/frontend/import_projects/store/actions_spec.js
+++ b/spec/frontend/import_projects/store/actions_spec.js
@@ -1,7 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
-import createFlash from '~/flash';
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'helpers/test_constants';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
@@ -12,41 +12,79 @@ import {
RECEIVE_IMPORT_SUCCESS,
RECEIVE_IMPORT_ERROR,
RECEIVE_JOBS_SUCCESS,
+ REQUEST_NAMESPACES,
+ RECEIVE_NAMESPACES_SUCCESS,
+ RECEIVE_NAMESPACES_ERROR,
+ SET_PAGE,
} from '~/import_projects/store/mutation_types';
-import {
- fetchRepos,
- fetchImport,
- receiveJobsSuccess,
- fetchJobs,
- clearJobsEtagPoll,
- stopJobsPolling,
-} from '~/import_projects/store/actions';
+import actionsFactory from '~/import_projects/store/actions';
+import { getImportTarget } from '~/import_projects/store/getters';
import state from '~/import_projects/store/state';
+import { STATUSES } from '~/import_projects/constants';
jest.mock('~/flash');
+const MOCK_ENDPOINT = `${TEST_HOST}/endpoint.json`;
+const endpoints = {
+ reposPath: MOCK_ENDPOINT,
+ importPath: MOCK_ENDPOINT,
+ jobsPath: MOCK_ENDPOINT,
+ namespacesPath: MOCK_ENDPOINT,
+};
+
+const {
+ clearJobsEtagPoll,
+ stopJobsPolling,
+ importAll,
+ fetchRepos,
+ fetchImport,
+ fetchJobs,
+ fetchNamespaces,
+ setPage,
+} = actionsFactory({
+ endpoints,
+});
+
describe('import_projects store actions', () => {
let localState;
- const repos = [{ id: 1 }, { id: 2 }];
- const importPayload = { newName: 'newName', targetNamespace: 'targetNamespace', repo: { id: 1 } };
+ const importRepoId = 1;
+ const otherImportRepoId = 2;
+ const defaultTargetNamespace = 'default';
+ const sanitizedName = 'sanitizedName';
+ const defaultImportTarget = { newName: sanitizedName, targetNamespace: defaultTargetNamespace };
beforeEach(() => {
- localState = state();
+ localState = {
+ ...state(),
+ defaultTargetNamespace,
+ repositories: [
+ { importSource: { id: importRepoId, sanitizedName }, importStatus: STATUSES.NONE },
+ {
+ importSource: { id: otherImportRepoId, sanitizedName: 's2' },
+ importStatus: STATUSES.NONE,
+ },
+ {
+ importSource: { id: 3, sanitizedName: 's3', incompatible: true },
+ importStatus: STATUSES.NONE,
+ },
+ ],
+ };
+
+ localState.getImportTarget = getImportTarget(localState);
});
describe('fetchRepos', () => {
let mock;
- const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] };
+ const payload = { imported_projects: [{}], provider_repos: [{}] };
beforeEach(() => {
- localState.reposPath = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
});
afterEach(() => mock.restore());
it('dispatches stopJobsPolling actions and commits REQUEST_REPOS, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, payload);
+ mock.onGet(MOCK_ENDPOINT).reply(200, payload);
return testAction(
fetchRepos,
@@ -64,7 +102,7 @@ describe('import_projects store actions', () => {
});
it('dispatches stopJobsPolling action and commits REQUEST_REPOS, RECEIVE_REPOS_ERROR mutations on an unsuccessful request', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ mock.onGet(MOCK_ENDPOINT).reply(500);
return testAction(
fetchRepos,
@@ -75,18 +113,39 @@ describe('import_projects store actions', () => {
);
});
- describe('when filtered', () => {
- beforeEach(() => {
- localState.filter = 'filter';
+ describe('when pagination is enabled', () => {
+ it('includes page in url query params', async () => {
+ const { fetchRepos: fetchReposWithPagination } = actionsFactory({
+ endpoints,
+ hasPagination: true,
+ });
+
+ let requestedUrl;
+ mock.onGet().reply(config => {
+ requestedUrl = config.url;
+ return [200, payload];
+ });
+
+ await testAction(
+ fetchReposWithPagination,
+ null,
+ localState,
+ expect.any(Array),
+ expect.any(Array),
+ );
+
+ expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localState.pageInfo.page}`);
});
+ });
+ describe('when filtered', () => {
it('fetches repos with filter applied', () => {
mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload);
return testAction(
fetchRepos,
null,
- localState,
+ { ...localState, filter: 'filter' },
[
{ type: REQUEST_REPOS },
{
@@ -104,7 +163,6 @@ describe('import_projects store actions', () => {
let mock;
beforeEach(() => {
- localState.importPath = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
});
@@ -112,15 +170,17 @@ describe('import_projects store actions', () => {
it('commits REQUEST_IMPORT and REQUEST_IMPORT_SUCCESS mutations on a successful request', () => {
const importedProject = { name: 'imported/project' };
- const importRepoId = importPayload.repo.id;
- mock.onPost(`${TEST_HOST}/endpoint.json`).reply(200, importedProject);
+ mock.onPost(MOCK_ENDPOINT).reply(200, importedProject);
return testAction(
fetchImport,
- importPayload,
+ importRepoId,
localState,
[
- { type: REQUEST_IMPORT, payload: importRepoId },
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
{
type: RECEIVE_IMPORT_SUCCESS,
payload: {
@@ -134,15 +194,18 @@ describe('import_projects store actions', () => {
});
it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows generic error message on an unsuccessful request', async () => {
- mock.onPost(`${TEST_HOST}/endpoint.json`).reply(500);
+ mock.onPost(MOCK_ENDPOINT).reply(500);
await testAction(
fetchImport,
- importPayload,
+ importRepoId,
localState,
[
- { type: REQUEST_IMPORT, payload: importPayload.repo.id },
- { type: RECEIVE_IMPORT_ERROR, payload: importPayload.repo.id },
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
+ { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
],
[],
);
@@ -152,15 +215,18 @@ describe('import_projects store actions', () => {
it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR and shows detailed error message on an unsuccessful request with errors fields in response', async () => {
const ERROR_MESSAGE = 'dummy';
- mock.onPost(`${TEST_HOST}/endpoint.json`).reply(500, { errors: ERROR_MESSAGE });
+ mock.onPost(MOCK_ENDPOINT).reply(500, { errors: ERROR_MESSAGE });
await testAction(
fetchImport,
- importPayload,
+ importRepoId,
localState,
[
- { type: REQUEST_IMPORT, payload: importPayload.repo.id },
- { type: RECEIVE_IMPORT_ERROR, payload: importPayload.repo.id },
+ {
+ type: REQUEST_IMPORT,
+ payload: { repoId: importRepoId, importTarget: defaultImportTarget },
+ },
+ { type: RECEIVE_IMPORT_ERROR, payload: importRepoId },
],
[],
);
@@ -169,24 +235,11 @@ describe('import_projects store actions', () => {
});
});
- describe('receiveJobsSuccess', () => {
- it(`commits ${RECEIVE_JOBS_SUCCESS} mutation`, () => {
- return testAction(
- receiveJobsSuccess,
- repos,
- localState,
- [{ type: RECEIVE_JOBS_SUCCESS, payload: repos }],
- [],
- );
- });
- });
-
describe('fetchJobs', () => {
let mock;
const updatedProjects = [{ name: 'imported/project' }, { name: 'provider/repo' }];
beforeEach(() => {
- localState.jobsPath = `${TEST_HOST}/endpoint.json`;
mock = new MockAdapter(axios);
});
@@ -198,7 +251,7 @@ describe('import_projects store actions', () => {
afterEach(() => mock.restore());
it('commits RECEIVE_JOBS_SUCCESS mutation on a successful request', async () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, updatedProjects);
+ mock.onGet(MOCK_ENDPOINT).reply(200, updatedProjects);
await testAction(
fetchJobs,
@@ -237,4 +290,78 @@ describe('import_projects store actions', () => {
});
});
});
+
+ describe('fetchNamespaces', () => {
+ let mock;
+ const namespaces = [{ full_name: 'test/ns1' }, { full_name: 'test_ns2' }];
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => mock.restore());
+
+ it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_SUCCESS on success', async () => {
+ mock.onGet(MOCK_ENDPOINT).reply(200, namespaces);
+
+ await testAction(
+ fetchNamespaces,
+ null,
+ localState,
+ [
+ { type: REQUEST_NAMESPACES },
+ {
+ type: RECEIVE_NAMESPACES_SUCCESS,
+ payload: convertObjectPropsToCamelCase(namespaces, { deep: true }),
+ },
+ ],
+ [],
+ );
+ });
+
+ it('commits REQUEST_NAMESPACES and RECEIVE_NAMESPACES_ERROR and shows generic error message on an unsuccessful request', async () => {
+ mock.onGet(MOCK_ENDPOINT).reply(500);
+
+ await testAction(
+ fetchNamespaces,
+ null,
+ localState,
+ [{ type: REQUEST_NAMESPACES }, { type: RECEIVE_NAMESPACES_ERROR }],
+ [],
+ );
+
+ expect(createFlash).toHaveBeenCalledWith('Requesting namespaces failed');
+ });
+ });
+
+ describe('importAll', () => {
+ it('dispatches multiple fetchImport actions', async () => {
+ await testAction(
+ importAll,
+ null,
+ localState,
+ [],
+ [
+ { type: 'fetchImport', payload: importRepoId },
+ { type: 'fetchImport', payload: otherImportRepoId },
+ ],
+ );
+ });
+
+ describe('setPage', () => {
+ it('dispatches fetchRepos and commits setPage when page number differs from current one', async () => {
+ await testAction(
+ setPage,
+ 2,
+ { ...localState, pageInfo: { page: 1 } },
+ [{ type: SET_PAGE, payload: 2 }],
+ [{ type: 'fetchRepos' }],
+ );
+ });
+
+ it('does not perform any action if page equals to current one', async () => {
+ await testAction(setPage, 2, { ...localState, pageInfo: { page: 2 } }, [], []);
+ });
+ });
+ });
});
diff --git a/spec/frontend/import_projects/store/getters_spec.js b/spec/frontend/import_projects/store/getters_spec.js
index 93d1ed89783..5c1ea25a684 100644
--- a/spec/frontend/import_projects/store/getters_spec.js
+++ b/spec/frontend/import_projects/store/getters_spec.js
@@ -1,12 +1,28 @@
import {
- namespaceSelectOptions,
+ isLoading,
isImportingAnyRepo,
- hasProviderRepos,
hasIncompatibleRepos,
- hasImportedProjects,
+ hasImportableRepos,
+ getImportTarget,
} from '~/import_projects/store/getters';
+import { STATUSES } from '~/import_projects/constants';
import state from '~/import_projects/store/state';
+const IMPORTED_REPO = {
+ importSource: {},
+ importedProject: { fullPath: 'some/path' },
+};
+
+const IMPORTABLE_REPO = {
+ importSource: { id: 'some-id', sanitizedName: 'sanitized' },
+ importedProject: null,
+ importStatus: STATUSES.NONE,
+};
+
+const INCOMPATIBLE_REPO = {
+ importSource: { incompatible: true },
+};
+
describe('import_projects store getters', () => {
let localState;
@@ -14,85 +30,87 @@ describe('import_projects store getters', () => {
localState = state();
});
- describe('namespaceSelectOptions', () => {
- const namespaces = [{ fullPath: 'namespace-0' }, { fullPath: 'namespace-1' }];
- const defaultTargetNamespace = 'current-user';
-
- it('returns an options array with a "Users" and "Groups" optgroups', () => {
- localState.namespaces = namespaces;
- localState.defaultTargetNamespace = defaultTargetNamespace;
-
- const optionsArray = namespaceSelectOptions(localState);
- const groupsGroup = optionsArray[0];
- const usersGroup = optionsArray[1];
-
- expect(groupsGroup.text).toBe('Groups');
- expect(usersGroup.text).toBe('Users');
-
- groupsGroup.children.forEach((child, index) => {
- expect(child.id).toBe(namespaces[index].fullPath);
- expect(child.text).toBe(namespaces[index].fullPath);
+ it.each`
+ isLoadingRepos | isLoadingNamespaces | isLoadingValue
+ ${false} | ${false} | ${false}
+ ${true} | ${false} | ${true}
+ ${false} | ${true} | ${true}
+ ${true} | ${true} | ${true}
+ `(
+ 'isLoading returns $isLoadingValue when isLoadingRepos is $isLoadingRepos and isLoadingNamespaces is $isLoadingNamespaces',
+ ({ isLoadingRepos, isLoadingNamespaces, isLoadingValue }) => {
+ Object.assign(localState, {
+ isLoadingRepos,
+ isLoadingNamespaces,
});
- expect(usersGroup.children.length).toBe(1);
- expect(usersGroup.children[0].id).toBe(defaultTargetNamespace);
- expect(usersGroup.children[0].text).toBe(defaultTargetNamespace);
- });
- });
-
- describe('isImportingAnyRepo', () => {
- it('returns true if there are any reposBeingImported', () => {
- localState.reposBeingImported = new Array(1);
-
- expect(isImportingAnyRepo(localState)).toBe(true);
- });
+ expect(isLoading(localState)).toBe(isLoadingValue);
+ },
+ );
+
+ it.each`
+ importStatus | value
+ ${STATUSES.NONE} | ${false}
+ ${STATUSES.SCHEDULING} | ${true}
+ ${STATUSES.SCHEDULED} | ${true}
+ ${STATUSES.STARTED} | ${true}
+ ${STATUSES.FINISHED} | ${false}
+ `(
+ 'isImportingAnyRepo returns $value when repo with $importStatus status is available',
+ ({ importStatus, value }) => {
+ localState.repositories = [{ importStatus }];
+
+ expect(isImportingAnyRepo(localState)).toBe(value);
+ },
+ );
- it('returns false if there are no reposBeingImported', () => {
- localState.reposBeingImported = [];
-
- expect(isImportingAnyRepo(localState)).toBe(false);
- });
- });
-
- describe('hasProviderRepos', () => {
- it('returns true if there are any providerRepos', () => {
- localState.providerRepos = new Array(1);
+ describe('hasIncompatibleRepos', () => {
+ it('returns true if there are any incompatible projects', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
- expect(hasProviderRepos(localState)).toBe(true);
+ expect(hasIncompatibleRepos(localState)).toBe(true);
});
- it('returns false if there are no providerRepos', () => {
- localState.providerRepos = [];
+ it('returns false if there are no incompatible projects', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO];
- expect(hasProviderRepos(localState)).toBe(false);
+ expect(hasIncompatibleRepos(localState)).toBe(false);
});
});
- describe('hasImportedProjects', () => {
- it('returns true if there are any importedProjects', () => {
- localState.importedProjects = new Array(1);
+ describe('hasImportableRepos', () => {
+ it('returns true if there are any importable projects ', () => {
+ localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
- expect(hasImportedProjects(localState)).toBe(true);
+ expect(hasImportableRepos(localState)).toBe(true);
});
- it('returns false if there are no importedProjects', () => {
- localState.importedProjects = [];
+ it('returns false if there are no importable projects', () => {
+ localState.repositories = [IMPORTED_REPO, INCOMPATIBLE_REPO];
- expect(hasImportedProjects(localState)).toBe(false);
+ expect(hasImportableRepos(localState)).toBe(false);
});
});
- describe('hasIncompatibleRepos', () => {
- it('returns true if there are any incompatibleProjects', () => {
- localState.incompatibleRepos = new Array(1);
+ describe('getImportTarget', () => {
+ it('returns default value if no custom target available', () => {
+ localState.defaultTargetNamespace = 'default';
+ localState.repositories = [IMPORTABLE_REPO];
- expect(hasIncompatibleRepos(localState)).toBe(true);
+ expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual({
+ newName: IMPORTABLE_REPO.importSource.sanitizedName,
+ targetNamespace: localState.defaultTargetNamespace,
+ });
});
- it('returns false if there are no incompatibleProjects', () => {
- localState.incompatibleRepos = [];
+ it('returns custom import target if available', () => {
+ const fakeTarget = { newName: 'something', targetNamespace: 'ns' };
+ localState.repositories = [IMPORTABLE_REPO];
+ localState.customImportTargets[IMPORTABLE_REPO.importSource.id] = fakeTarget;
- expect(hasIncompatibleRepos(localState)).toBe(false);
+ expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual(
+ fakeTarget,
+ );
});
});
});
diff --git a/spec/frontend/import_projects/store/mutations_spec.js b/spec/frontend/import_projects/store/mutations_spec.js
index 505545f7aa5..3672ec9f2c0 100644
--- a/spec/frontend/import_projects/store/mutations_spec.js
+++ b/spec/frontend/import_projects/store/mutations_spec.js
@@ -1,34 +1,303 @@
import * as types from '~/import_projects/store/mutation_types';
import mutations from '~/import_projects/store/mutations';
+import { STATUSES } from '~/import_projects/constants';
describe('import_projects store mutations', () => {
- describe(`${types.RECEIVE_IMPORT_SUCCESS}`, () => {
- it('removes repoId from reposBeingImported and providerRepos, adds to importedProjects', () => {
- const repoId = 1;
- const state = {
- reposBeingImported: [repoId],
- providerRepos: [{ id: repoId }],
+ let state;
+ const SOURCE_PROJECT = {
+ id: 1,
+ full_name: 'full/name',
+ sanitized_name: 'name',
+ provider_link: 'https://demo.link/full/name',
+ };
+ const IMPORTED_PROJECT = {
+ name: 'demo',
+ importSource: 'something',
+ providerLink: 'custom-link',
+ importStatus: 'status',
+ fullName: 'fullName',
+ };
+
+ describe(`${types.SET_FILTER}`, () => {
+ it('overwrites current filter value', () => {
+ state = { filter: 'some-value' };
+ const NEW_VALUE = 'new-value';
+
+ mutations[types.SET_FILTER](state, NEW_VALUE);
+
+ expect(state.filter).toBe(NEW_VALUE);
+ });
+ });
+
+ describe(`${types.REQUEST_REPOS}`, () => {
+ it('sets repos loading flag to true', () => {
+ state = {};
+
+ mutations[types.REQUEST_REPOS](state);
+
+ expect(state.isLoadingRepos).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_REPOS_SUCCESS}`, () => {
+ describe('for imported projects', () => {
+ const response = {
+ importedProjects: [IMPORTED_PROJECT],
+ providerRepos: [],
+ };
+
+ it('picks import status from response', () => {
+ state = {};
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.repositories[0].importStatus).toBe(IMPORTED_PROJECT.importStatus);
+ });
+
+ it('recreates importSource from response', () => {
+ state = {};
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.repositories[0].importSource).toStrictEqual(
+ expect.objectContaining({
+ fullName: IMPORTED_PROJECT.importSource,
+ sanitizedName: IMPORTED_PROJECT.name,
+ providerLink: IMPORTED_PROJECT.providerLink,
+ }),
+ );
+ });
+
+ it('passes project to importProject', () => {
+ state = {};
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(IMPORTED_PROJECT).toStrictEqual(
+ expect.objectContaining(state.repositories[0].importedProject),
+ );
+ });
+ });
+
+ describe('for importable projects', () => {
+ beforeEach(() => {
+ state = {};
+ const response = {
+ importedProjects: [],
+ providerRepos: [SOURCE_PROJECT],
+ };
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+ });
+
+ it('sets import status to none', () => {
+ expect(state.repositories[0].importStatus).toBe(STATUSES.NONE);
+ });
+
+ it('sets importSource to project', () => {
+ expect(state.repositories[0].importSource).toBe(SOURCE_PROJECT);
+ });
+ });
+
+ describe('for incompatible projects', () => {
+ const response = {
importedProjects: [],
+ providerRepos: [],
+ incompatibleRepos: [SOURCE_PROJECT],
};
- const importedProject = { id: repoId };
- mutations[types.RECEIVE_IMPORT_SUCCESS](state, { importedProject, repoId });
+ beforeEach(() => {
+ state = {};
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+ });
+
+ it('sets incompatible flag', () => {
+ expect(state.repositories[0].importSource.incompatible).toBe(true);
+ });
+
+ it('sets importSource to project', () => {
+ expect(state.repositories[0].importSource).toStrictEqual(
+ expect.objectContaining(SOURCE_PROJECT),
+ );
+ });
+ });
+
+ it('sets repos loading flag to false', () => {
+ const response = {
+ importedProjects: [],
+ providerRepos: [],
+ };
+ state = {};
+
+ mutations[types.RECEIVE_REPOS_SUCCESS](state, response);
+
+ expect(state.isLoadingRepos).toBe(false);
+ });
+ });
+
+ describe(`${types.RECEIVE_REPOS_ERROR}`, () => {
+ it('sets repos loading flag to false', () => {
+ state = {};
+
+ mutations[types.RECEIVE_REPOS_ERROR](state);
+
+ expect(state.isLoadingRepos).toBe(false);
+ });
+ });
+
+ describe(`${types.REQUEST_IMPORT}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.REQUEST_IMPORT](state, { repoId: REPO_ID, importTarget });
+ });
+
+ it(`sets status to ${STATUSES.SCHEDULING}`, () => {
+ expect(state.repositories[0].importStatus).toBe(STATUSES.SCHEDULING);
+ });
+ });
+
+ describe(`${types.RECEIVE_IMPORT_SUCCESS}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.RECEIVE_IMPORT_SUCCESS](state, {
+ repoId: REPO_ID,
+ importedProject: IMPORTED_PROJECT,
+ });
+ });
- expect(state.reposBeingImported.includes(repoId)).toBe(false);
- expect(state.providerRepos.some(repo => repo.id === repoId)).toBe(false);
- expect(state.importedProjects.some(repo => repo.id === repoId)).toBe(true);
+ it('sets import status', () => {
+ expect(state.repositories[0].importStatus).toBe(IMPORTED_PROJECT.importStatus);
+ });
+
+ it('sets imported project', () => {
+ expect(IMPORTED_PROJECT).toStrictEqual(
+ expect.objectContaining(state.repositories[0].importedProject),
+ );
+ });
+ });
+
+ describe(`${types.RECEIVE_IMPORT_ERROR}`, () => {
+ beforeEach(() => {
+ const REPO_ID = 1;
+ state = { repositories: [{ importSource: { id: REPO_ID } }] };
+
+ mutations[types.RECEIVE_IMPORT_ERROR](state, REPO_ID);
+ });
+
+ it(`resets import status to ${STATUSES.NONE}`, () => {
+ expect(state.repositories[0].importStatus).toBe(STATUSES.NONE);
});
});
describe(`${types.RECEIVE_JOBS_SUCCESS}`, () => {
- it('updates importStatus of existing importedProjects', () => {
+ it('updates import status of existing project', () => {
const repoId = 1;
- const state = { importedProjects: [{ id: repoId, importStatus: 'started' }] };
- const updatedProjects = [{ id: repoId, importStatus: 'finished' }];
+ state = {
+ repositories: [{ importedProject: { id: repoId }, importStatus: STATUSES.STARTED }],
+ };
+ const updatedProjects = [{ id: repoId, importStatus: STATUSES.FINISHED }];
mutations[types.RECEIVE_JOBS_SUCCESS](state, updatedProjects);
- expect(state.importedProjects[0].importStatus).toBe(updatedProjects[0].importStatus);
+ expect(state.repositories[0].importStatus).toBe(updatedProjects[0].importStatus);
+ });
+ });
+
+ describe(`${types.REQUEST_NAMESPACES}`, () => {
+ it('sets namespaces loading flag to true', () => {
+ state = {};
+
+ mutations[types.REQUEST_NAMESPACES](state);
+
+ expect(state.isLoadingNamespaces).toBe(true);
+ });
+ });
+
+ describe(`${types.RECEIVE_NAMESPACES_SUCCESS}`, () => {
+ const response = [{ fullPath: 'some/path' }];
+
+ beforeEach(() => {
+ state = {};
+ mutations[types.RECEIVE_NAMESPACES_SUCCESS](state, response);
+ });
+
+ it('stores namespaces to state', () => {
+ expect(state.namespaces).toStrictEqual(response);
+ });
+
+ it('sets namespaces loading flag to false', () => {
+ expect(state.isLoadingNamespaces).toBe(false);
+ });
+ });
+
+ describe(`${types.RECEIVE_NAMESPACES_ERROR}`, () => {
+ it('sets namespaces loading flag to false', () => {
+ state = {};
+
+ mutations[types.RECEIVE_NAMESPACES_ERROR](state);
+
+ expect(state.isLoadingNamespaces).toBe(false);
+ });
+ });
+
+ describe(`${types.SET_IMPORT_TARGET}`, () => {
+ const PROJECT = {
+ id: 2,
+ sanitizedName: 'sanitizedName',
+ };
+
+ it('stores custom target if it differs from defaults', () => {
+ state = { customImportTargets: {}, repositories: [{ importSource: PROJECT }] };
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+
+ mutations[types.SET_IMPORT_TARGET](state, { repoId: PROJECT.id, importTarget });
+ expect(state.customImportTargets[PROJECT.id]).toBe(importTarget);
+ });
+
+ it('removes custom target if it is equal to defaults', () => {
+ const importTarget = { targetNamespace: 'ns', newName: 'name ' };
+ state = {
+ defaultTargetNamespace: 'default',
+ customImportTargets: {
+ [PROJECT.id]: importTarget,
+ },
+ repositories: [{ importSource: PROJECT }],
+ };
+
+ mutations[types.SET_IMPORT_TARGET](state, {
+ repoId: PROJECT.id,
+ importTarget: {
+ targetNamespace: state.defaultTargetNamespace,
+ newName: PROJECT.sanitizedName,
+ },
+ });
+
+ expect(state.customImportTargets[SOURCE_PROJECT.id]).toBeUndefined();
+ });
+ });
+
+ describe(`${types.SET_PAGE_INFO}`, () => {
+ it('sets passed page info', () => {
+ state = {};
+ const pageInfo = { page: 1, total: 10 };
+
+ mutations[types.SET_PAGE_INFO](state, pageInfo);
+
+ expect(state.pageInfo).toBe(pageInfo);
+ });
+ });
+
+ describe(`${types.SET_PAGE}`, () => {
+ it('sets page number', () => {
+ const NEW_PAGE = 4;
+ state = { pageInfo: { page: 5 } };
+
+ mutations[types.SET_PAGE](state, NEW_PAGE);
+ expect(state.pageInfo.page).toBe(NEW_PAGE);
});
});
});
diff --git a/spec/frontend/import_projects/utils_spec.js b/spec/frontend/import_projects/utils_spec.js
new file mode 100644
index 00000000000..826b06d5a70
--- /dev/null
+++ b/spec/frontend/import_projects/utils_spec.js
@@ -0,0 +1,32 @@
+import { isProjectImportable } from '~/import_projects/utils';
+import { STATUSES } from '~/import_projects/constants';
+
+describe('import_projects utils', () => {
+ describe('isProjectImportable', () => {
+ it.each`
+ status | result
+ ${STATUSES.FINISHED} | ${false}
+ ${STATUSES.FAILED} | ${false}
+ ${STATUSES.SCHEDULED} | ${false}
+ ${STATUSES.STARTED} | ${false}
+ ${STATUSES.NONE} | ${true}
+ ${STATUSES.SCHEDULING} | ${false}
+ `('returns $result when project is compatible and status is $status', ({ status, result }) => {
+ expect(
+ isProjectImportable({
+ importStatus: status,
+ importSource: { incompatible: false },
+ }),
+ ).toBe(result);
+ });
+
+ it('returns false if project is not compatible', () => {
+ expect(
+ isProjectImportable({
+ importStatus: STATUSES.NONE,
+ importSource: { incompatible: true },
+ }),
+ ).toBe(false);
+ });
+ });
+});