Commit 9b0ad368 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'design-repo-actions' into 'master'

Design Repo Sync Status - Actions

Closes #34336

See merge request gitlab-org/gitlab!19590
parents 52e736a6 a6c0096c
...@@ -208,4 +208,14 @@ export default { ...@@ -208,4 +208,14 @@ export default {
const url = Api.buildUrl(this.geoDesignsPath); const url = Api.buildUrl(this.geoDesignsPath);
return axios.get(url, { params }); return axios.get(url, { params });
}, },
initiateAllGeoDesignSyncs(action) {
const url = Api.buildUrl(this.geoDesignsPath);
return axios.post(`${url}/${action}`, {});
},
initiateGeoDesignSync({ projectId, action }) {
const url = Api.buildUrl(this.geoDesignsPath);
return axios.put(`${url}/${projectId}/${action}`, {});
},
}; };
<script> <script>
import { GlLink } from '@gitlab/ui'; import { mapActions } from 'vuex';
import { GlLink, GlButton } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { ACTION_TYPES } from '../store/constants';
import GeoDesignStatus from './geo_design_status.vue'; import GeoDesignStatus from './geo_design_status.vue';
import GeoDesignTimeAgo from './geo_design_time_ago.vue'; import GeoDesignTimeAgo from './geo_design_time_ago.vue';
...@@ -8,6 +10,7 @@ export default { ...@@ -8,6 +10,7 @@ export default {
name: 'GeoDesign', name: 'GeoDesign',
components: { components: {
GlLink, GlLink,
GlButton,
GeoDesignTimeAgo, GeoDesignTimeAgo,
GeoDesignStatus, GeoDesignStatus,
}, },
...@@ -62,6 +65,10 @@ export default { ...@@ -62,6 +65,10 @@ export default {
], ],
}; };
}, },
methods: {
...mapActions(['initiateDesignSync']),
},
actionTypes: ACTION_TYPES,
}; };
</script> </script>
...@@ -69,6 +76,12 @@ export default { ...@@ -69,6 +76,12 @@ export default {
<div class="card"> <div class="card">
<div class="card-header d-flex align-center"> <div class="card-header d-flex align-center">
<gl-link class="font-weight-bold" :href="`/${name}`" target="_blank">{{ name }}</gl-link> <gl-link class="font-weight-bold" :href="`/${name}`" target="_blank">{{ name }}</gl-link>
<div class="ml-auto">
<gl-button
@click="initiateDesignSync({ projectId, name, action: $options.actionTypes.RESYNC })"
>{{ __('Resync') }}</gl-button
>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="d-flex flex-column flex-md-row"> <div class="d-flex flex-column flex-md-row">
......
...@@ -26,7 +26,7 @@ export default { ...@@ -26,7 +26,7 @@ export default {
<template> <template>
<gl-empty-state :title="__('Design Sync Not Enabled')" :svg-path="geoSvgPath"> <gl-empty-state :title="__('Design Sync Not Enabled')" :svg-path="geoSvgPath">
<template v-slot:description> <template #description>
<div class="text-center"> <div class="text-center">
<p> <p>
{{ {{
......
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
<template> <template>
<gl-empty-state :title="__('No Design Repositories match this filter')" :svg-path="issuesSvgPath"> <gl-empty-state :title="__('No Design Repositories match this filter')" :svg-path="issuesSvgPath">
<template v-slot:description> <template #description>
<div class="text-center"> <div class="text-center">
<p>{{ __('Adjust your filters/search criteria above.') }}</p> <p>{{ __('Adjust your filters/search criteria above.') }}</p>
<p v-html="linkText"></p> <p v-html="linkText"></p>
......
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { debounce } from 'underscore'; import { debounce } from 'underscore';
import { GlTabs, GlTab, GlFormInput } from '@gitlab/ui'; import { GlTabs, GlTab, GlFormInput, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { DEFAULT_SEARCH_DELAY } from '../store/constants'; import Icon from '~/vue_shared/components/icon.vue';
import { DEFAULT_SEARCH_DELAY, ACTION_TYPES } from '../store/constants';
export default { export default {
name: 'GeoDesignsFilterBar', name: 'GeoDesignsFilterBar',
...@@ -10,6 +11,9 @@ export default { ...@@ -10,6 +11,9 @@ export default {
GlTabs, GlTabs,
GlTab, GlTab,
GlFormInput, GlFormInput,
GlDropdown,
GlDropdownItem,
Icon,
}, },
computed: { computed: {
...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter']), ...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter']),
...@@ -24,12 +28,13 @@ export default { ...@@ -24,12 +28,13 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(['setFilter', 'setSearch', 'fetchDesigns']), ...mapActions(['setFilter', 'setSearch', 'fetchDesigns', 'initiateAllDesignSyncs']),
filterChange(filterIndex) { filterChange(filterIndex) {
this.setFilter(filterIndex); this.setFilter(filterIndex);
this.fetchDesigns(); this.fetchDesigns();
}, },
}, },
actionTypes: ACTION_TYPES,
}; };
</script> </script>
...@@ -41,9 +46,21 @@ export default { ...@@ -41,9 +46,21 @@ export default {
:title="filter" :title="filter"
title-item-class="text-capitalize" title-item-class="text-capitalize"
/> />
<template v-slot:tabs-end> <template #tabs-end>
<div class="d-flex align-items-center ml-auto"> <div class="d-flex align-items-center ml-auto">
<gl-form-input v-model="search" type="text" :placeholder="__(`Filter by name...`)" /> <gl-form-input v-model="search" type="text" :placeholder="__(`Filter by name...`)" />
<gl-dropdown class="ml-2">
<template #button-content>
<span>
<icon name="cloud-gear" />
{{ __('Batch operations') }}
<icon name="chevron-down" />
</span>
</template>
<gl-dropdown-item @click="initiateAllDesignSyncs($options.actionTypes.RESYNC)">{{
__('Resync all designs')
}}</gl-dropdown-item>
</gl-dropdown>
</div> </div>
</template> </template>
</gl-tabs> </gl-tabs>
......
...@@ -47,6 +47,49 @@ export const fetchDesigns = ({ state, dispatch }) => { ...@@ -47,6 +47,49 @@ export const fetchDesigns = ({ state, dispatch }) => {
}); });
}; };
// Initiate All Design Syncs
export const requestInitiateAllDesignSyncs = ({ commit }) =>
commit(types.REQUEST_INITIATE_ALL_DESIGN_SYNCS);
export const receiveInitiateAllDesignSyncsSuccess = ({ commit, dispatch }) => {
commit(types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS);
dispatch('fetchDesigns');
};
export const receiveInitiateAllDesignSyncsError = ({ commit }) => {
createFlash(__(`There was an error syncing the Design Repositories.`));
commit(types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR);
};
export const initiateAllDesignSyncs = ({ dispatch }, action) => {
dispatch('requestInitiateAllDesignSyncs');
Api.initiateAllGeoDesignSyncs(action)
.then(() => dispatch('receiveInitiateAllDesignSyncsSuccess'))
.catch(() => {
dispatch('receiveInitiateAllDesignSyncsError');
});
};
// Initiate Design Sync
export const requestInitiateDesignSync = ({ commit }) => commit(types.REQUEST_INITIATE_DESIGN_SYNC);
export const receiveInitiateDesignSyncSuccess = ({ commit, dispatch }) => {
commit(types.RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS);
dispatch('fetchDesigns');
};
export const receiveInitiateDesignSyncError = ({ commit }, { name }) => {
createFlash(__(`There was an error syncing project '${name}'`));
commit(types.RECEIVE_INITIATE_DESIGN_SYNC_ERROR);
};
export const initiateDesignSync = ({ dispatch }, { projectId, name, action }) => {
dispatch('requestInitiateDesignSync');
Api.initiateGeoDesignSync({ projectId, action })
.then(() => dispatch('receiveInitiateDesignSyncSuccess'))
.catch(() => {
dispatch('receiveInitiateDesignSyncError', { name });
});
};
// Filtering/Pagination // Filtering/Pagination
export const setFilter = ({ commit }, filterIndex) => { export const setFilter = ({ commit }, filterIndex) => {
commit(types.SET_FILTER, filterIndex); commit(types.SET_FILTER, filterIndex);
......
...@@ -22,3 +22,10 @@ export const STATUS_ICON_CLASS = { ...@@ -22,3 +22,10 @@ export const STATUS_ICON_CLASS = {
}; };
export const DEFAULT_SEARCH_DELAY = 500; export const DEFAULT_SEARCH_DELAY = 500;
export const ACTION_TYPES = {
RESYNC: 'resync',
// Below not implemented yet
REVERIFY: 'reverify',
FORCE_REDOWNLOAD: 'force_redownload',
};
...@@ -5,3 +5,12 @@ export const SET_PAGE = 'SET_PAGE'; ...@@ -5,3 +5,12 @@ export const SET_PAGE = 'SET_PAGE';
export const REQUEST_DESIGNS = 'REQUEST_DESIGNS'; export const REQUEST_DESIGNS = 'REQUEST_DESIGNS';
export const RECEIVE_DESIGNS_SUCCESS = 'RECEIVE_DESIGNS_SUCCESS'; export const RECEIVE_DESIGNS_SUCCESS = 'RECEIVE_DESIGNS_SUCCESS';
export const RECEIVE_DESIGNS_ERROR = 'RECEIVE_DESIGNS_ERROR'; export const RECEIVE_DESIGNS_ERROR = 'RECEIVE_DESIGNS_ERROR';
export const REQUEST_INITIATE_ALL_DESIGN_SYNCS = 'REQUEST_INITIATE_ALL_DESIGN_SYNCS';
export const RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS =
'RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS';
export const RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR = 'RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR';
export const REQUEST_INITIATE_DESIGN_SYNC = 'REQUEST_INITIATE_DESIGN_SYNC';
export const RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS = 'RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS';
export const RECEIVE_INITIATE_DESIGN_SYNC_ERROR = 'RECEIVE_INITIATE_DESIGN_SYNC_ERROR';
...@@ -27,4 +27,22 @@ export default { ...@@ -27,4 +27,22 @@ export default {
state.pageSize = 0; state.pageSize = 0;
state.totalDesigns = 0; state.totalDesigns = 0;
}, },
[types.REQUEST_INITIATE_ALL_DESIGN_SYNCS](state) {
state.isLoading = true;
},
[types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR](state) {
state.isLoading = false;
},
[types.REQUEST_INITIATE_DESIGN_SYNC](state) {
state.isLoading = true;
},
[types.RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_INITIATE_DESIGN_SYNC_ERROR](state) {
state.isLoading = false;
},
}; };
...@@ -556,19 +556,70 @@ describe('Api', () => { ...@@ -556,19 +556,70 @@ describe('Api', () => {
}); });
}); });
describe('getGeoDesigns', () => { describe('GeoDesigns', () => {
it('fetches designs', () => { let expectedUrl;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/geo_replication/designs`; let apiResponse;
const apiResponse = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }]; let mockParams;
const mockParams = { page: 1 };
beforeEach(() => {
jest.spyOn(Api, 'buildUrl').mockReturnValue(expectedUrl); expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/geo_replication/designs`;
jest.spyOn(axios, 'get'); });
mock.onGet(expectedUrl).replyOnce(200, apiResponse);
describe('getGeoDesigns', () => {
return Api.getGeoDesigns(mockParams).then(({ data }) => { it('fetches designs', () => {
expect(data).toEqual(apiResponse); apiResponse = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];
expect(axios.get).toHaveBeenCalledWith(expectedUrl, { params: mockParams }); mockParams = { page: 1 };
jest.spyOn(Api, 'buildUrl').mockReturnValue(expectedUrl);
jest.spyOn(axios, 'get');
mock.onGet(expectedUrl).replyOnce(200, apiResponse);
return Api.getGeoDesigns(mockParams).then(({ data }) => {
expect(data).toEqual(apiResponse);
expect(axios.get).toHaveBeenCalledWith(expectedUrl, { params: mockParams });
});
});
});
describe('initiateAllGeoDesignSyncs', () => {
it('POSTs with correct action', () => {
apiResponse = [{ status: 'ok' }];
mockParams = {};
const mockAction = 'reverify';
jest.spyOn(Api, 'buildUrl').mockReturnValue(expectedUrl);
jest.spyOn(axios, 'post');
mock.onPost(`${expectedUrl}/${mockAction}`).replyOnce(201, apiResponse);
return Api.initiateAllGeoDesignSyncs(mockAction).then(({ data }) => {
expect(data).toEqual(apiResponse);
expect(axios.post).toHaveBeenCalledWith(`${expectedUrl}/${mockAction}`, mockParams);
});
});
});
describe('initiateGeoDesignSync', () => {
it('PUTs with correct action and projectId', () => {
apiResponse = [{ status: 'ok' }];
mockParams = {};
const mockAction = 'reverify';
const mockProjectId = 1;
jest.spyOn(Api, 'buildUrl').mockReturnValue(expectedUrl);
jest.spyOn(axios, 'put');
mock.onPut(`${expectedUrl}/${mockProjectId}/${mockAction}`).replyOnce(201, apiResponse);
return Api.initiateGeoDesignSync({ projectId: mockProjectId, action: mockAction }).then(
({ data }) => {
expect(data).toEqual(apiResponse);
expect(axios.put).toHaveBeenCalledWith(
`${expectedUrl}/${mockProjectId}/${mockAction}`,
mockParams,
);
},
);
}); });
}); });
}); });
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui'; import { GlLink, GlButton } from '@gitlab/ui';
import GeoDesign from 'ee/geo_designs/components/geo_design.vue'; import GeoDesign from 'ee/geo_designs/components/geo_design.vue';
import store from 'ee/geo_designs/store'; import store from 'ee/geo_designs/store';
import { ACTION_TYPES } from 'ee/geo_designs/store/constants';
import { MOCK_BASIC_FETCH_DATA_MAP } from '../mock_data'; import { MOCK_BASIC_FETCH_DATA_MAP } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -12,6 +13,10 @@ describe('GeoDesignsApp', () => { ...@@ -12,6 +13,10 @@ describe('GeoDesignsApp', () => {
let wrapper; let wrapper;
const mockDesign = MOCK_BASIC_FETCH_DATA_MAP.data[0]; const mockDesign = MOCK_BASIC_FETCH_DATA_MAP.data[0];
const actionSpies = {
initiateDesignSync: jest.fn(),
};
const propsData = { const propsData = {
name: mockDesign.name, name: mockDesign.name,
projectId: mockDesign.projectId, projectId: mockDesign.projectId,
...@@ -22,10 +27,13 @@ describe('GeoDesignsApp', () => { ...@@ -22,10 +27,13 @@ describe('GeoDesignsApp', () => {
}; };
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(localVue.extend(GeoDesign), { wrapper = mount(localVue.extend(GeoDesign), {
localVue, localVue,
store, store,
propsData, propsData,
methods: {
...actionSpies,
},
}); });
}; };
...@@ -35,6 +43,7 @@ describe('GeoDesignsApp', () => { ...@@ -35,6 +43,7 @@ describe('GeoDesignsApp', () => {
const findCard = () => wrapper.find('.card'); const findCard = () => wrapper.find('.card');
const findGlLink = () => findCard().find(GlLink); const findGlLink = () => findCard().find(GlLink);
const findGlButton = () => findCard().find(GlButton);
const findCardHeader = () => findCard().find('.card-header'); const findCardHeader = () => findCard().find('.card-header');
const findCardBody = () => findCard().find('.card-body'); const findCardBody = () => findCard().find('.card-body');
...@@ -58,5 +67,20 @@ describe('GeoDesignsApp', () => { ...@@ -58,5 +67,20 @@ describe('GeoDesignsApp', () => {
it('GlLink renders', () => { it('GlLink renders', () => {
expect(findGlLink().exists()).toBe(true); expect(findGlLink().exists()).toBe(true);
}); });
describe('ReSync Button', () => {
it('renders', () => {
expect(findGlButton().exists()).toBe(true);
});
it('calls initiateDesignSyncs when clicked', () => {
findGlButton().trigger('click');
expect(actionSpies.initiateDesignSync).toHaveBeenCalledWith({
projectId: propsData.projectId,
name: propsData.name,
action: ACTION_TYPES.RESYNC,
});
});
});
}); });
}); });
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, mount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import { GlTabs, GlTab, GlFormInput } from '@gitlab/ui'; import { GlTabs, GlTab, GlFormInput, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import GeoDesignsFilterBar from 'ee/geo_designs/components/geo_designs_filter_bar.vue'; import GeoDesignsFilterBar from 'ee/geo_designs/components/geo_designs_filter_bar.vue';
import store from 'ee/geo_designs/store'; import store from 'ee/geo_designs/store';
import { DEFAULT_SEARCH_DELAY } from 'ee/geo_designs/store/constants'; import { DEFAULT_SEARCH_DELAY } from 'ee/geo_designs/store/constants';
...@@ -15,6 +15,7 @@ describe('GeoDesignsFilterBar', () => { ...@@ -15,6 +15,7 @@ describe('GeoDesignsFilterBar', () => {
setSearch: jest.fn(), setSearch: jest.fn(),
setFilter: jest.fn(), setFilter: jest.fn(),
fetchDesigns: jest.fn(), fetchDesigns: jest.fn(),
initiateAllDesignSyncs: jest.fn(),
}; };
const createComponent = () => { const createComponent = () => {
...@@ -34,6 +35,8 @@ describe('GeoDesignsFilterBar', () => { ...@@ -34,6 +35,8 @@ describe('GeoDesignsFilterBar', () => {
const findGlTabsContainer = () => wrapper.find(GlTabs); const findGlTabsContainer = () => wrapper.find(GlTabs);
const findGlTab = () => findGlTabsContainer().findAll(GlTab); const findGlTab = () => findGlTabsContainer().findAll(GlTab);
const findGlFormInput = () => findGlTabsContainer().find(GlFormInput); const findGlFormInput = () => findGlTabsContainer().find(GlFormInput);
const findGlDropdown = () => findGlTabsContainer().find(GlDropdown);
const findGlDropdownItem = () => findGlTabsContainer().find(GlDropdownItem);
describe('template', () => { describe('template', () => {
beforeEach(() => { beforeEach(() => {
...@@ -58,6 +61,22 @@ describe('GeoDesignsFilterBar', () => { ...@@ -58,6 +61,22 @@ describe('GeoDesignsFilterBar', () => {
it('renders GlFormInput', () => { it('renders GlFormInput', () => {
expect(findGlFormInput().exists()).toBe(true); expect(findGlFormInput().exists()).toBe(true);
}); });
it('renders GlDropdown', () => {
expect(findGlDropdown().exists()).toBe(true);
});
describe('GlDropDownItem', () => {
it('renders', () => {
expect(findGlDropdownItem().exists()).toBe(true);
});
it('calls initiateAllDesignSyncs when clicked', () => {
const innerButton = findGlDropdownItem().find('button');
innerButton.trigger('click');
expect(actionSpies.initiateAllDesignSyncs).toHaveBeenCalled();
});
});
}); });
describe('when search changes', () => { describe('when search changes', () => {
......
...@@ -38,3 +38,7 @@ export const MOCK_BASIC_FETCH_DATA_MAP = { ...@@ -38,3 +38,7 @@ export const MOCK_BASIC_FETCH_DATA_MAP = {
perPage: MOCK_BASIC_FETCH_RESPONSE.headers['x-per-page'], perPage: MOCK_BASIC_FETCH_RESPONSE.headers['x-per-page'],
total: MOCK_BASIC_FETCH_RESPONSE.headers['x-total'], total: MOCK_BASIC_FETCH_RESPONSE.headers['x-total'],
}; };
export const MOCK_BASIC_POST_RESPONSE = {
status: 'ok',
};
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import flash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as actions from 'ee/geo_designs/store/actions'; import * as actions from 'ee/geo_designs/store/actions';
import * as types from 'ee/geo_designs/store/mutation_types'; import * as types from 'ee/geo_designs/store/mutation_types';
import createState from 'ee/geo_designs/store/state'; import createState from 'ee/geo_designs/store/state';
import flash from '~/flash'; import { ACTION_TYPES } from 'ee/geo_designs/store/constants';
import axios from '~/lib/utils/axios_utils'; import {
import { MOCK_BASIC_FETCH_DATA_MAP, MOCK_BASIC_FETCH_RESPONSE } from '../mock_data'; MOCK_BASIC_FETCH_DATA_MAP,
MOCK_BASIC_FETCH_RESPONSE,
MOCK_BASIC_POST_RESPONSE,
} from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -42,16 +47,18 @@ describe('GeoDesigns Store Actions', () => { ...@@ -42,16 +47,18 @@ describe('GeoDesigns Store Actions', () => {
}); });
describe('receiveDesignsError', () => { describe('receiveDesignsError', () => {
it('should commit mutation RECEIVE_DESIGNS_ERROR and call flash', done => { it('should commit mutation RECEIVE_DESIGNS_ERROR', () => {
testAction( testAction(
actions.receiveDesignsError, actions.receiveDesignsError,
null, null,
state, state,
[{ type: types.RECEIVE_DESIGNS_ERROR }], [{ type: types.RECEIVE_DESIGNS_ERROR }],
[], [],
done, () => {
expect(flash).toHaveBeenCalledTimes(1);
flash.mockClear();
},
); );
expect(flash).toHaveBeenCalledTimes(1);
}); });
}); });
...@@ -98,7 +105,6 @@ describe('GeoDesigns Store Actions', () => { ...@@ -98,7 +105,6 @@ describe('GeoDesigns Store Actions', () => {
describe('queryParams', () => { describe('queryParams', () => {
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios);
mock mock
.onGet() .onGet()
.replyOnce(200, MOCK_BASIC_FETCH_RESPONSE.data, MOCK_BASIC_FETCH_RESPONSE.headers); .replyOnce(200, MOCK_BASIC_FETCH_RESPONSE.data, MOCK_BASIC_FETCH_RESPONSE.headers);
...@@ -162,6 +168,192 @@ describe('GeoDesigns Store Actions', () => { ...@@ -162,6 +168,192 @@ describe('GeoDesigns Store Actions', () => {
}); });
}); });
describe('requestInitiateAllDesignSyncs', () => {
it('should commit mutation REQUEST_INITIATE_ALL_DESIGN_SYNCS', done => {
testAction(
actions.requestInitiateAllDesignSyncs,
null,
state,
[{ type: types.REQUEST_INITIATE_ALL_DESIGN_SYNCS }],
[],
done,
);
});
});
describe('receiveInitiateAllDesignSyncsSuccess', () => {
it('should commit mutation RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS and fetchDesigns', done => {
testAction(
actions.receiveInitiateAllDesignSyncsSuccess,
null,
state,
[{ type: types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS }],
[{ type: 'fetchDesigns' }],
done,
);
});
});
describe('receiveInitiateAllDesignSyncsError', () => {
it('should commit mutation RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR', () => {
testAction(
actions.receiveInitiateAllDesignSyncsError,
ACTION_TYPES.RESYNC,
state,
[{ type: types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR }],
[],
() => {
expect(flash).toHaveBeenCalledTimes(1);
flash.mockClear();
},
);
});
});
describe('initiateAllDesignSyncs', () => {
let action;
describe('on success', () => {
beforeEach(() => {
action = ACTION_TYPES.RESYNC;
mock.onPost().replyOnce(201, MOCK_BASIC_POST_RESPONSE);
});
it('should dispatch the request and success actions', done => {
testAction(
actions.initiateAllDesignSyncs,
action,
state,
[],
[
{ type: 'requestInitiateAllDesignSyncs' },
{ type: 'receiveInitiateAllDesignSyncsSuccess' },
],
done,
);
});
});
describe('on error', () => {
beforeEach(() => {
action = ACTION_TYPES.RESYNC;
mock.onPost().replyOnce(500);
});
it('should dispatch the request and error actions', done => {
testAction(
actions.initiateAllDesignSyncs,
action,
state,
[],
[
{ type: 'requestInitiateAllDesignSyncs' },
{ type: 'receiveInitiateAllDesignSyncsError' },
],
done,
);
});
});
});
describe('requestInitiateDesignSync', () => {
it('should commit mutation REQUEST_INITIATE_DESIGN_SYNC', done => {
testAction(
actions.requestInitiateDesignSync,
null,
state,
[{ type: types.REQUEST_INITIATE_DESIGN_SYNC }],
[],
done,
);
});
});
describe('receiveInitiateDesignSyncSuccess', () => {
it('should commit mutation RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS and fetchDesigns', done => {
testAction(
actions.receiveInitiateDesignSyncSuccess,
null,
state,
[{ type: types.RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS }],
[{ type: 'fetchDesigns' }],
done,
);
});
});
describe('receiveInitiateDesignSyncError', () => {
it('should commit mutation RECEIVE_INITIATE_DESIGN_SYNC_ERROR', () => {
testAction(
actions.receiveInitiateDesignSyncError,
{ action: ACTION_TYPES.RESYNC, projectId: 1, projectName: 'test' },
state,
[{ type: types.RECEIVE_INITIATE_DESIGN_SYNC_ERROR }],
[],
() => {
expect(flash).toHaveBeenCalledTimes(1);
flash.mockClear();
},
);
});
});
describe('initiateDesignSync', () => {
let action;
let projectId;
let name;
describe('on success', () => {
beforeEach(() => {
action = ACTION_TYPES.RESYNC;
projectId = 1;
name = 'test';
mock.onPut().replyOnce(201, MOCK_BASIC_POST_RESPONSE);
});
it('should dispatch the request and success actions', done => {
testAction(
actions.initiateDesignSync,
{ projectId, name, action },
state,
[],
[{ type: 'requestInitiateDesignSync' }, { type: 'receiveInitiateDesignSyncSuccess' }],
done,
);
});
});
describe('on error', () => {
beforeEach(() => {
action = ACTION_TYPES.RESYNC;
projectId = 1;
name = 'test';
mock.onPut().replyOnce(500);
});
it('should dispatch the request and error actions', done => {
testAction(
actions.initiateDesignSync,
{ projectId, name, action },
state,
[],
[
{ type: 'requestInitiateDesignSync' },
{
type: 'receiveInitiateDesignSyncError',
payload: { name: 'test' },
},
],
done,
);
});
});
});
describe('setFilter', () => { describe('setFilter', () => {
it('should commit mutation SET_FILTER', done => { it('should commit mutation SET_FILTER', done => {
const testValue = 1; const testValue = 1;
......
...@@ -118,4 +118,23 @@ describe('GeoDesigns Store Mutations', () => { ...@@ -118,4 +118,23 @@ describe('GeoDesigns Store Mutations', () => {
expect(state.totalDesigns).toEqual(0); expect(state.totalDesigns).toEqual(0);
}); });
}); });
describe.each`
mutation | loadingBefore | loadingAfter
${types.REQUEST_INITIATE_ALL_DESIGN_SYNCS} | ${false} | ${true}
${types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_SUCCESS} | ${true} | ${false}
${types.RECEIVE_INITIATE_ALL_DESIGN_SYNCS_ERROR} | ${true} | ${false}
${types.REQUEST_INITIATE_DESIGN_SYNC} | ${false} | ${true}
${types.RECEIVE_INITIATE_DESIGN_SYNC_SUCCESS} | ${true} | ${false}
${types.RECEIVE_INITIATE_DESIGN_SYNC_ERROR} | ${true} | ${false}
`(`Sync Mutations: `, ({ mutation, loadingBefore, loadingAfter }) => {
describe(`${mutation}`, () => {
it(`sets isLoading to ${loadingAfter}`, () => {
state.isLoading = loadingBefore;
mutations[mutation](state);
expect(state.isLoading).toEqual(loadingAfter);
});
});
});
}); });
...@@ -2567,6 +2567,9 @@ msgstr "" ...@@ -2567,6 +2567,9 @@ msgstr ""
msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo."
msgstr "" msgstr ""
msgid "Batch operations"
msgstr ""
msgid "BatchComments|Delete all pending comments" msgid "BatchComments|Delete all pending comments"
msgstr "" msgstr ""
...@@ -15434,6 +15437,12 @@ msgstr "" ...@@ -15434,6 +15437,12 @@ msgstr ""
msgid "Resume replication" msgid "Resume replication"
msgstr "" msgstr ""
msgid "Resync"
msgstr ""
msgid "Resync all designs"
msgstr ""
msgid "Retry" msgid "Retry"
msgstr "" msgstr ""
...@@ -18332,6 +18341,9 @@ msgstr "" ...@@ -18332,6 +18341,9 @@ msgstr ""
msgid "There was an error subscribing to this label." msgid "There was an error subscribing to this label."
msgstr "" msgstr ""
msgid "There was an error syncing the Design Repositories."
msgstr ""
msgid "There was an error trying to validate your query" msgid "There was an error trying to validate your query"
msgstr "" msgstr ""
......
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