Commit 392b9abc authored by Illya Klymov's avatar Illya Klymov Committed by Mark Florian

Update ImportProjectsTable component

- introduce support for incompatible repos rows
- refactor storage according to latest Vuex guidelines
- added slots for warning messages
parent 631b3f2a
<script> <script>
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import ImportedProjectTableRow from './imported_project_table_row.vue'; import ImportedProjectTableRow from './imported_project_table_row.vue';
import ProviderRepoTableRow from './provider_repo_table_row.vue'; import ProviderRepoTableRow from './provider_repo_table_row.vue';
import IncompatibleRepoTableRow from './incompatible_repo_table_row.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
const reposFetchThrottleDelay = 1000; const reposFetchThrottleDelay = 1000;
...@@ -15,8 +15,9 @@ export default { ...@@ -15,8 +15,9 @@ export default {
components: { components: {
ImportedProjectTableRow, ImportedProjectTableRow,
ProviderRepoTableRow, ProviderRepoTableRow,
LoadingButton, IncompatibleRepoTableRow,
GlLoadingIcon, GlLoadingIcon,
GlButton,
}, },
props: { props: {
providerTitle: { providerTitle: {
...@@ -26,8 +27,25 @@ export default { ...@@ -26,8 +27,25 @@ export default {
}, },
computed: { computed: {
...mapState(['importedProjects', 'providerRepos', 'isLoadingRepos', 'filter']), ...mapState([
...mapGetters(['isImportingAnyRepo', 'hasProviderRepos', 'hasImportedProjects']), 'importedProjects',
'providerRepos',
'incompatibleRepos',
'isLoadingRepos',
'filter',
]),
...mapGetters([
'isImportingAnyRepo',
'hasProviderRepos',
'hasImportedProjects',
'hasIncompatibleRepos',
]),
importAllButtonText() {
return this.hasIncompatibleRepos
? __('Import all compatible repositories')
: __('Import all repositories');
},
emptyStateText() { emptyStateText() {
return sprintf(__('No %{providerTitle} repositories found'), { return sprintf(__('No %{providerTitle} repositories found'), {
...@@ -68,7 +86,6 @@ export default { ...@@ -68,7 +86,6 @@ export default {
}, },
throttledFetchRepos: throttle(function fetch() { throttledFetchRepos: throttle(function fetch() {
eventHub.$off('importAll');
this.fetchRepos(); this.fetchRepos();
}, reposFetchThrottleDelay), }, reposFetchThrottleDelay),
}, },
...@@ -80,17 +97,24 @@ export default { ...@@ -80,17 +97,24 @@ export default {
<p class="light text-nowrap mt-2"> <p class="light text-nowrap mt-2">
{{ s__('ImportProjects|Select the projects you want to import') }} {{ s__('ImportProjects|Select the projects you want to import') }}
</p> </p>
<template v-if="hasIncompatibleRepos">
<div class="d-flex justify-content-between align-items-end flex-wrap mb-3"> <slot name="incompatible-repos-warning"> </slot>
<loading-button </template>
container-class="btn btn-success js-import-all" <div
v-if="!isLoadingRepos"
class="d-flex justify-content-between align-items-end flex-wrap mb-3"
>
<gl-button
variant="success"
:loading="isImportingAnyRepo" :loading="isImportingAnyRepo"
:label="__('Import all repositories')"
:disabled="!hasProviderRepos" :disabled="!hasProviderRepos"
type="button" type="button"
@click="importAll" @click="importAll"
/> >
<form novalidate @submit.prevent> {{ importAllButtonText }}
</gl-button>
<slot name="actions"></slot>
<form class="gl-ml-auto" novalidate @submit.prevent>
<input <input
:value="filter" :value="filter"
data-qa-selector="githubish_import_filter_field" data-qa-selector="githubish_import_filter_field"
...@@ -109,7 +133,10 @@ export default { ...@@ -109,7 +133,10 @@ export default {
class="js-loading-button-icon import-projects-loading-icon" class="js-loading-button-icon import-projects-loading-icon"
size="md" size="md"
/> />
<div v-else-if="hasProviderRepos || hasImportedProjects" class="table-responsive"> <div
v-else-if="hasProviderRepos || hasImportedProjects || hasIncompatibleRepos"
class="table-responsive"
>
<table class="table import-table"> <table class="table import-table">
<thead> <thead>
<th class="import-jobs-from-col">{{ fromHeaderText }}</th> <th class="import-jobs-from-col">{{ fromHeaderText }}</th>
...@@ -124,6 +151,11 @@ export default { ...@@ -124,6 +151,11 @@ export default {
:project="project" :project="project"
/> />
<provider-repo-table-row v-for="repo in providerRepos" :key="repo.id" :repo="repo" /> <provider-repo-table-row v-for="repo in providerRepos" :key="repo.id" :repo="repo" />
<incompatible-repo-table-row
v-for="repo in incompatibleRepos"
:key="repo.id"
:repo="repo"
/>
</tbody> </tbody>
</table> </table>
</div> </div>
......
<script>
import { GlBadge } from '@gitlab/ui';
export default {
components: {
GlBadge,
},
props: {
repo: {
type: Object,
required: true,
},
},
};
</script>
<template>
<tr class="import-row">
<td>
<a :href="repo.providerLink" rel="noreferrer noopener" target="_blank">
{{ repo.fullName }}
</a>
</td>
<td></td>
<td></td>
<td>
<gl-badge variant="danger">{{ __('Incompatible project') }}</gl-badge>
</td>
</tr>
</template>
...@@ -53,7 +53,11 @@ export default { ...@@ -53,7 +53,11 @@ export default {
}, },
created() { created() {
eventHub.$on('importAll', () => this.importRepo()); eventHub.$on('importAll', this.importRepo);
},
beforeDestroy() {
eventHub.$off('importAll', this.importRepo);
}, },
methods: { methods: {
......
import Vue from 'vue'; import Vue from 'vue';
import { mapActions } from 'vuex';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
import ImportProjectsTable from './components/import_projects_table.vue'; import ImportProjectsTable from './components/import_projects_table.vue';
import { parseBoolean } from '../lib/utils/common_utils'; import { parseBoolean } from '../lib/utils/common_utils';
...@@ -7,42 +6,44 @@ import createStore from './store'; ...@@ -7,42 +6,44 @@ import createStore from './store';
Vue.use(Translate); Vue.use(Translate);
export default function mountImportProjectsTable(mountElement) { export function initStoreFromElement(element) {
if (!mountElement) return undefined;
const { const {
reposPath, reposPath,
provider, provider,
providerTitle,
canSelectNamespace, canSelectNamespace,
jobsPath, jobsPath,
importPath, importPath,
ciCdOnly, ciCdOnly,
} = mountElement.dataset; } = element.dataset;
const store = createStore(); return createStore({
return new Vue({ reposPath,
el: mountElement, provider,
store, jobsPath,
importPath,
defaultTargetNamespace: gon.current_username,
ciCdOnly: parseBoolean(ciCdOnly),
canSelectNamespace: parseBoolean(canSelectNamespace),
});
}
created() { export function initPropsFromElement(element) {
this.setInitialData({ return {
reposPath, providerTitle: element.dataset.providerTitle,
provider, };
jobsPath, }
importPath,
defaultTargetNamespace: gon.current_username,
ciCdOnly: parseBoolean(ciCdOnly),
canSelectNamespace: parseBoolean(canSelectNamespace),
});
},
methods: { export default function mountImportProjectsTable(mountElement) {
...mapActions(['setInitialData', 'setFilter']), if (!mountElement) return undefined;
},
const store = initStoreFromElement(mountElement);
const props = initPropsFromElement(mountElement);
return new Vue({
el: mountElement,
store,
render(createElement) { render(createElement) {
return createElement(ImportProjectsTable, { props: { providerTitle } }); return createElement(ImportProjectsTable, { props });
}, },
}); });
} }
...@@ -19,23 +19,18 @@ export const restartJobsPolling = () => { ...@@ -19,23 +19,18 @@ export const restartJobsPolling = () => {
if (eTagPoll) eTagPoll.restart(); if (eTagPoll) eTagPoll.restart();
}; };
export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
export const setFilter = ({ commit }, filter) => commit(types.SET_FILTER, filter); export const setFilter = ({ commit }, filter) => commit(types.SET_FILTER, filter);
export const requestRepos = ({ commit }, repos) => commit(types.REQUEST_REPOS, repos); export const fetchRepos = ({ state, dispatch, commit }) => {
export const receiveReposSuccess = ({ commit }, repos) =>
commit(types.RECEIVE_REPOS_SUCCESS, repos);
export const receiveReposError = ({ commit }) => commit(types.RECEIVE_REPOS_ERROR);
export const fetchRepos = ({ state, dispatch }) => {
dispatch('stopJobsPolling'); dispatch('stopJobsPolling');
dispatch('requestRepos'); commit(types.REQUEST_REPOS);
const { provider } = state; const { provider } = state;
return axios return axios
.get(reposPathWithFilter(state)) .get(reposPathWithFilter(state))
.then(({ data }) => .then(({ data }) =>
dispatch('receiveReposSuccess', convertObjectPropsToCamelCase(data, { deep: true })), commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
) )
.then(() => dispatch('fetchJobs')) .then(() => dispatch('fetchJobs'))
.catch(() => { .catch(() => {
...@@ -45,19 +40,14 @@ export const fetchRepos = ({ state, dispatch }) => { ...@@ -45,19 +40,14 @@ export const fetchRepos = ({ state, dispatch }) => {
}), }),
); );
dispatch('receiveReposError'); commit(types.RECEIVE_REPOS_ERROR);
}); });
}; };
export const requestImport = ({ commit, state }, repoId) => { export const fetchImport = ({ state, commit }, { newName, targetNamespace, repo }) => {
if (!state.reposBeingImported.includes(repoId)) commit(types.REQUEST_IMPORT, repoId); if (!state.reposBeingImported.includes(repo.id)) {
}; commit(types.REQUEST_IMPORT, repo.id);
export const receiveImportSuccess = ({ commit }, { importedProject, repoId }) => }
commit(types.RECEIVE_IMPORT_SUCCESS, { importedProject, repoId });
export const receiveImportError = ({ commit }, repoId) =>
commit(types.RECEIVE_IMPORT_ERROR, repoId);
export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, repo }) => {
dispatch('requestImport', repo.id);
return axios return axios
.post(state.importPath, { .post(state.importPath, {
...@@ -67,7 +57,7 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep ...@@ -67,7 +57,7 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep
target_namespace: targetNamespace, target_namespace: targetNamespace,
}) })
.then(({ data }) => .then(({ data }) =>
dispatch('receiveImportSuccess', { commit(types.RECEIVE_IMPORT_SUCCESS, {
importedProject: convertObjectPropsToCamelCase(data, { deep: true }), importedProject: convertObjectPropsToCamelCase(data, { deep: true }),
repoId: repo.id, repoId: repo.id,
}), }),
...@@ -75,13 +65,14 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep ...@@ -75,13 +65,14 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep
.catch(() => { .catch(() => {
createFlash(s__('ImportProjects|Importing the project failed')); createFlash(s__('ImportProjects|Importing the project failed'));
dispatch('receiveImportError', { repoId: repo.id }); commit(types.RECEIVE_IMPORT_ERROR, repo.id);
}); });
}; };
export const receiveJobsSuccess = ({ commit }, updatedProjects) => export const receiveJobsSuccess = ({ commit }, updatedProjects) =>
commit(types.RECEIVE_JOBS_SUCCESS, updatedProjects); commit(types.RECEIVE_JOBS_SUCCESS, updatedProjects);
export const fetchJobs = ({ state, dispatch }) => {
export const fetchJobs = ({ state, commit, dispatch }) => {
const { filter } = state; const { filter } = state;
if (eTagPoll) { if (eTagPoll) {
...@@ -95,7 +86,7 @@ export const fetchJobs = ({ state, dispatch }) => { ...@@ -95,7 +86,7 @@ export const fetchJobs = ({ state, dispatch }) => {
}, },
method: 'fetchJobs', method: 'fetchJobs',
successCallback: ({ data }) => successCallback: ({ data }) =>
dispatch('receiveJobsSuccess', convertObjectPropsToCamelCase(data, { deep: true })), commit(types.RECEIVE_JOBS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })),
errorCallback: () => errorCallback: () =>
createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')), createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')),
data: { filter }, data: { filter },
......
...@@ -21,6 +21,8 @@ export const hasProviderRepos = state => state.providerRepos.length > 0; ...@@ -21,6 +21,8 @@ export const hasProviderRepos = state => state.providerRepos.length > 0;
export const hasImportedProjects = state => state.importedProjects.length > 0; export const hasImportedProjects = state => state.importedProjects.length > 0;
export const hasIncompatibleRepos = state => state.incompatibleRepos.length > 0;
export const reposPathWithFilter = ({ reposPath, filter = '' }) => export const reposPathWithFilter = ({ reposPath, filter = '' }) =>
filter ? `${reposPath}?filter=${filter}` : reposPath; filter ? `${reposPath}?filter=${filter}` : reposPath;
export const jobsPathWithFilter = ({ jobsPath, filter = '' }) => export const jobsPathWithFilter = ({ jobsPath, filter = '' }) =>
......
...@@ -9,9 +9,9 @@ Vue.use(Vuex); ...@@ -9,9 +9,9 @@ Vue.use(Vuex);
export { state, actions, getters, mutations }; export { state, actions, getters, mutations };
export default () => export default initialState =>
new Vuex.Store({ new Vuex.Store({
state: state(), state: { ...state(), ...initialState },
actions, actions,
mutations, mutations,
getters, getters,
......
export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
export const REQUEST_REPOS = 'REQUEST_REPOS'; export const REQUEST_REPOS = 'REQUEST_REPOS';
export const RECEIVE_REPOS_SUCCESS = 'RECEIVE_REPOS_SUCCESS'; export const RECEIVE_REPOS_SUCCESS = 'RECEIVE_REPOS_SUCCESS';
export const RECEIVE_REPOS_ERROR = 'RECEIVE_REPOS_ERROR'; export const RECEIVE_REPOS_ERROR = 'RECEIVE_REPOS_ERROR';
......
...@@ -2,10 +2,6 @@ import Vue from 'vue'; ...@@ -2,10 +2,6 @@ import Vue from 'vue';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
[types.SET_INITIAL_DATA](state, data) {
Object.assign(state, data);
},
[types.SET_FILTER](state, filter) { [types.SET_FILTER](state, filter) {
state.filter = filter; state.filter = filter;
}, },
...@@ -14,11 +10,15 @@ export default { ...@@ -14,11 +10,15 @@ export default {
state.isLoadingRepos = true; state.isLoadingRepos = true;
}, },
[types.RECEIVE_REPOS_SUCCESS](state, { importedProjects, providerRepos, namespaces }) { [types.RECEIVE_REPOS_SUCCESS](
state,
{ importedProjects, providerRepos, incompatibleRepos, namespaces },
) {
state.isLoadingRepos = false; state.isLoadingRepos = false;
state.importedProjects = importedProjects; state.importedProjects = importedProjects;
state.providerRepos = providerRepos; state.providerRepos = providerRepos;
state.incompatibleRepos = incompatibleRepos ?? [];
state.namespaces = namespaces; state.namespaces = namespaces;
}, },
......
...@@ -7,6 +7,7 @@ export default () => ({ ...@@ -7,6 +7,7 @@ export default () => ({
currentUsername: '', currentUsername: '',
importedProjects: [], importedProjects: [],
providerRepos: [], providerRepos: [],
incompatibleRepos: [],
namespaces: [], namespaces: [],
reposBeingImported: [], reposBeingImported: [],
isLoadingRepos: false, isLoadingRepos: false,
......
...@@ -11690,6 +11690,9 @@ msgstr "" ...@@ -11690,6 +11690,9 @@ msgstr ""
msgid "Import all compatible projects" msgid "Import all compatible projects"
msgstr "" msgstr ""
msgid "Import all compatible repositories"
msgstr ""
msgid "Import all projects" msgid "Import all projects"
msgstr "" msgstr ""
...@@ -11879,6 +11882,9 @@ msgstr "" ...@@ -11879,6 +11882,9 @@ msgstr ""
msgid "Incompatible options set!" msgid "Incompatible options set!"
msgstr "" msgstr ""
msgid "Incompatible project"
msgstr ""
msgid "Indent" msgid "Indent"
msgstr "" msgstr ""
......
...@@ -6,7 +6,7 @@ import STATUS_MAP, { STATUSES } from '~/import_projects/constants'; ...@@ -6,7 +6,7 @@ import STATUS_MAP, { STATUSES } from '~/import_projects/constants';
describe('ProviderRepoTableRow', () => { describe('ProviderRepoTableRow', () => {
let vm; let vm;
const fetchImport = jest.fn((context, data) => actions.requestImport(context, data)); const fetchImport = jest.fn();
const importPath = '/import-path'; const importPath = '/import-path';
const defaultTargetNamespace = 'user'; const defaultTargetNamespace = 'user';
const ciCdOnly = true; const ciCdOnly = true;
...@@ -17,11 +17,11 @@ describe('ProviderRepoTableRow', () => { ...@@ -17,11 +17,11 @@ describe('ProviderRepoTableRow', () => {
providerLink: 'providerLink', providerLink: 'providerLink',
}; };
function initStore() { function initStore(initialState) {
const stubbedActions = { ...actions, fetchImport }; const stubbedActions = { ...actions, fetchImport };
const store = new Vuex.Store({ const store = new Vuex.Store({
state: state(), state: { ...state(), ...initialState },
actions: stubbedActions, actions: stubbedActions,
mutations, mutations,
getters, getters,
...@@ -30,12 +30,11 @@ describe('ProviderRepoTableRow', () => { ...@@ -30,12 +30,11 @@ describe('ProviderRepoTableRow', () => {
return store; return store;
} }
function mountComponent() { function mountComponent(initialState) {
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
const store = initStore(); const store = initStore({ importPath, defaultTargetNamespace, ciCdOnly, ...initialState });
store.dispatch('setInitialData', { importPath, defaultTargetNamespace, ciCdOnly });
const component = mount(providerRepoTableRow, { const component = mount(providerRepoTableRow, {
localVue, localVue,
......
...@@ -4,7 +4,6 @@ import { TEST_HOST } from 'helpers/test_constants'; ...@@ -4,7 +4,6 @@ import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { import {
SET_INITIAL_DATA,
REQUEST_REPOS, REQUEST_REPOS,
RECEIVE_REPOS_SUCCESS, RECEIVE_REPOS_SUCCESS,
RECEIVE_REPOS_ERROR, RECEIVE_REPOS_ERROR,
...@@ -14,14 +13,7 @@ import { ...@@ -14,14 +13,7 @@ import {
RECEIVE_JOBS_SUCCESS, RECEIVE_JOBS_SUCCESS,
} from '~/import_projects/store/mutation_types'; } from '~/import_projects/store/mutation_types';
import { import {
setInitialData,
requestRepos,
receiveReposSuccess,
receiveReposError,
fetchRepos, fetchRepos,
requestImport,
receiveImportSuccess,
receiveImportError,
fetchImport, fetchImport,
receiveJobsSuccess, receiveJobsSuccess,
fetchJobs, fetchJobs,
...@@ -32,7 +24,6 @@ import state from '~/import_projects/store/state'; ...@@ -32,7 +24,6 @@ import state from '~/import_projects/store/state';
describe('import_projects store actions', () => { describe('import_projects store actions', () => {
let localState; let localState;
const repoId = 1;
const repos = [{ id: 1 }, { id: 2 }]; const repos = [{ id: 1 }, { id: 2 }];
const importPayload = { newName: 'newName', targetNamespace: 'targetNamespace', repo: { id: 1 } }; const importPayload = { newName: 'newName', targetNamespace: 'targetNamespace', repo: { id: 1 } };
...@@ -40,61 +31,6 @@ describe('import_projects store actions', () => { ...@@ -40,61 +31,6 @@ describe('import_projects store actions', () => {
localState = state(); localState = state();
}); });
describe('setInitialData', () => {
it(`commits ${SET_INITIAL_DATA} mutation`, done => {
const initialData = {
reposPath: 'reposPath',
provider: 'provider',
jobsPath: 'jobsPath',
importPath: 'impapp/assets/javascripts/vue_shared/components/select2_select.vueortPath',
defaultTargetNamespace: 'defaultTargetNamespace',
ciCdOnly: 'ciCdOnly',
canSelectNamespace: 'canSelectNamespace',
};
testAction(
setInitialData,
initialData,
localState,
[{ type: SET_INITIAL_DATA, payload: initialData }],
[],
done,
);
});
});
describe('requestRepos', () => {
it(`requestRepos commits ${REQUEST_REPOS} mutation`, done => {
testAction(
requestRepos,
null,
localState,
[{ type: REQUEST_REPOS, payload: null }],
[],
done,
);
});
});
describe('receiveReposSuccess', () => {
it(`commits ${RECEIVE_REPOS_SUCCESS} mutation`, done => {
testAction(
receiveReposSuccess,
repos,
localState,
[{ type: RECEIVE_REPOS_SUCCESS, payload: repos }],
[],
done,
);
});
});
describe('receiveReposError', () => {
it(`commits ${RECEIVE_REPOS_ERROR} mutation`, done => {
testAction(receiveReposError, repos, localState, [{ type: RECEIVE_REPOS_ERROR }], [], done);
});
});
describe('fetchRepos', () => { describe('fetchRepos', () => {
let mock; let mock;
const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] }; const payload = { imported_projects: [{}], provider_repos: [{}], namespaces: [{}] };
...@@ -106,39 +42,33 @@ describe('import_projects store actions', () => { ...@@ -106,39 +42,33 @@ describe('import_projects store actions', () => {
afterEach(() => mock.restore()); afterEach(() => mock.restore());
it('dispatches stopJobsPolling, requestRepos and receiveReposSuccess actions on a successful request', done => { 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(`${TEST_HOST}/endpoint.json`).reply(200, payload);
testAction( return testAction(
fetchRepos, fetchRepos,
null, null,
localState, localState,
[],
[ [
{ type: 'stopJobsPolling' }, { type: REQUEST_REPOS },
{ type: 'requestRepos' },
{ {
type: 'receiveReposSuccess', type: RECEIVE_REPOS_SUCCESS,
payload: convertObjectPropsToCamelCase(payload, { deep: true }), payload: convertObjectPropsToCamelCase(payload, { deep: true }),
}, },
{
type: 'fetchJobs',
},
], ],
done, [{ type: 'stopJobsPolling' }, { type: 'fetchJobs' }],
); );
}); });
it('dispatches stopJobsPolling, requestRepos and receiveReposError actions on an unsuccessful request', done => { 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(`${TEST_HOST}/endpoint.json`).reply(500);
testAction( return testAction(
fetchRepos, fetchRepos,
null, null,
localState, localState,
[], [{ type: REQUEST_REPOS }, { type: RECEIVE_REPOS_ERROR }],
[{ type: 'stopJobsPolling' }, { type: 'requestRepos' }, { type: 'receiveReposError' }], [{ type: 'stopJobsPolling' }],
done,
); );
}); });
...@@ -147,72 +77,26 @@ describe('import_projects store actions', () => { ...@@ -147,72 +77,26 @@ describe('import_projects store actions', () => {
localState.filter = 'filter'; localState.filter = 'filter';
}); });
it('fetches repos with filter applied', done => { it('fetches repos with filter applied', () => {
mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload); mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload);
testAction( return testAction(
fetchRepos, fetchRepos,
null, null,
localState, localState,
[],
[ [
{ type: 'stopJobsPolling' }, { type: REQUEST_REPOS },
{ type: 'requestRepos' },
{ {
type: 'receiveReposSuccess', type: RECEIVE_REPOS_SUCCESS,
payload: convertObjectPropsToCamelCase(payload, { deep: true }), payload: convertObjectPropsToCamelCase(payload, { deep: true }),
}, },
{
type: 'fetchJobs',
},
], ],
done, [{ type: 'stopJobsPolling' }, { type: 'fetchJobs' }],
); );
}); });
}); });
}); });
describe('requestImport', () => {
it(`commits ${REQUEST_IMPORT} mutation`, done => {
testAction(
requestImport,
repoId,
localState,
[{ type: REQUEST_IMPORT, payload: repoId }],
[],
done,
);
});
});
describe('receiveImportSuccess', () => {
it(`commits ${RECEIVE_IMPORT_SUCCESS} mutation`, done => {
const payload = { importedProject: { name: 'imported/project' }, repoId: 2 };
testAction(
receiveImportSuccess,
payload,
localState,
[{ type: RECEIVE_IMPORT_SUCCESS, payload }],
[],
done,
);
});
});
describe('receiveImportError', () => {
it(`commits ${RECEIVE_IMPORT_ERROR} mutation`, done => {
testAction(
receiveImportError,
repoId,
localState,
[{ type: RECEIVE_IMPORT_ERROR, payload: repoId }],
[],
done,
);
});
});
describe('fetchImport', () => { describe('fetchImport', () => {
let mock; let mock;
...@@ -223,56 +107,53 @@ describe('import_projects store actions', () => { ...@@ -223,56 +107,53 @@ describe('import_projects store actions', () => {
afterEach(() => mock.restore()); afterEach(() => mock.restore());
it('dispatches requestImport and receiveImportSuccess actions on a successful request', done => { it('commits REQUEST_IMPORT and REQUEST_IMPORT_SUCCESS mutations on a successful request', () => {
const importedProject = { name: 'imported/project' }; const importedProject = { name: 'imported/project' };
const importRepoId = importPayload.repo.id; const importRepoId = importPayload.repo.id;
mock.onPost(`${TEST_HOST}/endpoint.json`).reply(200, importedProject); mock.onPost(`${TEST_HOST}/endpoint.json`).reply(200, importedProject);
testAction( return testAction(
fetchImport, fetchImport,
importPayload, importPayload,
localState, localState,
[],
[ [
{ type: 'requestImport', payload: importRepoId }, { type: REQUEST_IMPORT, payload: importRepoId },
{ {
type: 'receiveImportSuccess', type: RECEIVE_IMPORT_SUCCESS,
payload: { payload: {
importedProject: convertObjectPropsToCamelCase(importedProject, { deep: true }), importedProject: convertObjectPropsToCamelCase(importedProject, { deep: true }),
repoId: importRepoId, repoId: importRepoId,
}, },
}, },
], ],
done, [],
); );
}); });
it('dispatches requestImport and receiveImportSuccess actions on an unsuccessful request', done => { it('commits REQUEST_IMPORT and RECEIVE_IMPORT_ERROR on an unsuccessful request', () => {
mock.onPost(`${TEST_HOST}/endpoint.json`).reply(500); mock.onPost(`${TEST_HOST}/endpoint.json`).reply(500);
testAction( return testAction(
fetchImport, fetchImport,
importPayload, importPayload,
localState, localState,
[],
[ [
{ type: 'requestImport', payload: importPayload.repo.id }, { type: REQUEST_IMPORT, payload: importPayload.repo.id },
{ type: 'receiveImportError', payload: { repoId: importPayload.repo.id } }, { type: RECEIVE_IMPORT_ERROR, payload: importPayload.repo.id },
], ],
done, [],
); );
}); });
}); });
describe('receiveJobsSuccess', () => { describe('receiveJobsSuccess', () => {
it(`commits ${RECEIVE_JOBS_SUCCESS} mutation`, done => { it(`commits ${RECEIVE_JOBS_SUCCESS} mutation`, () => {
testAction( return testAction(
receiveJobsSuccess, receiveJobsSuccess,
repos, repos,
localState, localState,
[{ type: RECEIVE_JOBS_SUCCESS, payload: repos }], [{ type: RECEIVE_JOBS_SUCCESS, payload: repos }],
[], [],
done,
); );
}); });
}); });
...@@ -293,21 +174,20 @@ describe('import_projects store actions', () => { ...@@ -293,21 +174,20 @@ describe('import_projects store actions', () => {
afterEach(() => mock.restore()); afterEach(() => mock.restore());
it('dispatches requestJobs and receiveJobsSuccess actions on a successful request', done => { it('commits RECEIVE_JOBS_SUCCESS mutation on a successful request', async () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, updatedProjects); mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, updatedProjects);
testAction( await testAction(
fetchJobs, fetchJobs,
null, null,
localState, localState,
[],
[ [
{ {
type: 'receiveJobsSuccess', type: RECEIVE_JOBS_SUCCESS,
payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }), payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
}, },
], ],
done, [],
); );
}); });
...@@ -316,21 +196,20 @@ describe('import_projects store actions', () => { ...@@ -316,21 +196,20 @@ describe('import_projects store actions', () => {
localState.filter = 'filter'; localState.filter = 'filter';
}); });
it('fetches realtime changes with filter applied', done => { it('fetches realtime changes with filter applied', () => {
mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, updatedProjects); mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, updatedProjects);
testAction( return testAction(
fetchJobs, fetchJobs,
null, null,
localState, localState,
[],
[ [
{ {
type: 'receiveJobsSuccess', type: RECEIVE_JOBS_SUCCESS,
payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }), payload: convertObjectPropsToCamelCase(updatedProjects, { deep: true }),
}, },
], ],
done, [],
); );
}); });
}); });
......
...@@ -2,6 +2,7 @@ import { ...@@ -2,6 +2,7 @@ import {
namespaceSelectOptions, namespaceSelectOptions,
isImportingAnyRepo, isImportingAnyRepo,
hasProviderRepos, hasProviderRepos,
hasIncompatibleRepos,
hasImportedProjects, hasImportedProjects,
} from '~/import_projects/store/getters'; } from '~/import_projects/store/getters';
import state from '~/import_projects/store/state'; import state from '~/import_projects/store/state';
...@@ -80,4 +81,18 @@ describe('import_projects store getters', () => { ...@@ -80,4 +81,18 @@ describe('import_projects store getters', () => {
expect(hasImportedProjects(localState)).toBe(false); expect(hasImportedProjects(localState)).toBe(false);
}); });
}); });
describe('hasIncompatibleRepos', () => {
it('returns true if there are any incompatibleProjects', () => {
localState.incompatibleRepos = new Array(1);
expect(hasIncompatibleRepos(localState)).toBe(true);
});
it('returns false if there are no incompatibleProjects', () => {
localState.incompatibleRepos = [];
expect(hasIncompatibleRepos(localState)).toBe(false);
});
});
}); });
# frozen_string_literal: true
require 'spec_helper'
describe ProviderRepoSerializer do
it 'represents ProviderRepoEntity entities' do
expect(described_class.entity_class).to eq(ProviderRepoEntity)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment