Commit 5db86bcc authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Natalia Tepluhina

Add new basic registry explorer app

- store
- router
- basic pages
- haml entrypoints
- js entry points
- feature flag
- update list code to not overlap
parent 57be66b9
import initRegistryImages from '~/registry/list'; import initRegistryImages from '~/registry/list/index';
import registryExplorer from '~/registry/explorer/index';
document.addEventListener('DOMContentLoaded', initRegistryImages); document.addEventListener('DOMContentLoaded', () => {
initRegistryImages();
registryExplorer();
});
import initRegistryImages from '~/registry/list/index'; import initRegistryImages from '~/registry/list/index';
import registryExplorer from '~/registry/explorer/index';
document.addEventListener('DOMContentLoaded', initRegistryImages); document.addEventListener('DOMContentLoaded', () => {
initRegistryImages();
registryExplorer();
});
import { __ } from '~/locale';
export const FETCH_IMAGES_LIST_ERROR_MESSAGE = __(
'Something went wrong while fetching the packages list.',
);
export const FETCH_TAGS_LIST_ERROR_MESSAGE = __(
'Something went wrong while fetching the tags list.',
);
export const DELETE_IMAGE_ERROR_MESSAGE = __('Something went wrong while deleting the image.');
export const DELETE_IMAGE_SUCCESS_MESSAGE = __('Image deleted successfully');
export const DELETE_TAG_ERROR_MESSAGE = __('Something went wrong while deleting the tag.');
export const DELETE_TAG_SUCCESS_MESSAGE = __('Tag deleted successfully');
export const DELETE_TAGS_ERROR_MESSAGE = __('Something went wrong while deleting the tags.');
export const DELETE_TAGS_SUCCESS_MESSAGE = __('Tags deleted successfully');
export const DEFAULT_PAGE = 1;
export const DEFAULT_PAGE_SIZE = 10;
export const GROUP_PAGE_TYPE = 'groups';
export const LIST_KEY_TAG = 'name';
export const LIST_KEY_IMAGE_ID = 'short_revision';
export const LIST_KEY_SIZE = 'total_size';
export const LIST_KEY_LAST_UPDATED = 'created_at';
export const LIST_KEY_ACTIONS = 'actions';
export const LIST_KEY_CHECKBOX = 'checkbox';
export const LIST_LABEL_TAG = __('Tag');
export const LIST_LABEL_IMAGE_ID = __('Image ID');
export const LIST_LABEL_SIZE = __('Size');
export const LIST_LABEL_LAST_UPDATED = __('Last Updated');
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import RegistryExplorer from './pages/index.vue';
import { createStore } from './stores';
import createRouter from './router';
Vue.use(Translate);
export default () => {
const el = document.getElementById('js-container-registry');
if (!el) {
return null;
}
const { endpoint } = el.dataset;
const store = createStore();
const router = createRouter(endpoint, store);
store.dispatch('setInitialState', el.dataset);
return new Vue({
el,
store,
router,
components: {
RegistryExplorer,
},
render(createElement) {
return createElement('registry-explorer');
},
});
};
<script>
export default {};
</script>
<template>
<div></div>
</template>
<script>
export default {};
</script>
<template>
<div class="position-relative">
<transition name="slide">
<router-view />
</transition>
</div>
</template>
<script>
export default {};
</script>
<template>
<div></div>
</template>
import Vue from 'vue';
import VueRouter from 'vue-router';
import { __ } from '~/locale';
import List from './pages/list.vue';
import Details from './pages/details.vue';
Vue.use(VueRouter);
export default function createRouter(base, store) {
const router = new VueRouter({
base,
mode: 'history',
routes: [
{
name: 'list',
path: '/',
component: List,
meta: {
name: __('Container Registry'),
},
beforeEnter: (to, from, next) => {
store.dispatch('requestImagesList');
next();
},
},
{
name: 'details',
path: '/:id',
component: Details,
meta: {
name: __('Tags'),
},
beforeEnter: (to, from, next) => {
store.dispatch('requestTagsList', { id: to.params.id });
next();
},
},
],
});
return router;
}
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import * as types from './mutation_types';
import {
FETCH_IMAGES_LIST_ERROR_MESSAGE,
DEFAULT_PAGE,
DEFAULT_PAGE_SIZE,
FETCH_TAGS_LIST_ERROR_MESSAGE,
DELETE_TAG_SUCCESS_MESSAGE,
DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
DELETE_IMAGE_SUCCESS_MESSAGE,
} from '../constants';
export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data);
export const receiveImagesListSuccess = ({ commit }, { data, headers }) => {
commit(types.SET_IMAGES_LIST_SUCCESS, data);
commit(types.SET_PAGINATION, headers);
};
export const receiveTagsListSuccess = ({ commit }, { data, headers }) => {
commit(types.SET_TAGS_LIST_SUCCESS, data);
commit(types.SET_TAGS_PAGINATION, headers);
};
export const requestImagesList = ({ commit, dispatch, state }, pagination = {}) => {
commit(types.SET_MAIN_LOADING, true);
const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
return axios
.get(state.config.endpoint, { params: { page, per_page: perPage } })
.then(({ data, headers }) => {
dispatch('receiveImagesListSuccess', { data, headers });
})
.catch(() => {
createFlash(FETCH_IMAGES_LIST_ERROR_MESSAGE);
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
export const requestTagsList = ({ commit, dispatch }, { pagination = {}, id }) => {
commit(types.SET_MAIN_LOADING, true);
const url = window.atob(id);
const { page = DEFAULT_PAGE, perPage = DEFAULT_PAGE_SIZE } = pagination;
return axios
.get(url, { params: { page, per_page: perPage } })
.then(({ data, headers }) => {
dispatch('receiveTagsListSuccess', { data, headers });
})
.catch(() => {
createFlash(FETCH_TAGS_LIST_ERROR_MESSAGE);
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
export const requestDeleteTag = ({ commit, dispatch, state }, { tag, imageId }) => {
commit(types.SET_MAIN_LOADING, true);
return axios
.delete(tag.destroy_path)
.then(() => {
createFlash(DELETE_TAG_SUCCESS_MESSAGE, 'success');
dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId });
})
.catch(() => {
createFlash(DELETE_TAG_ERROR_MESSAGE);
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
export const requestDeleteTags = ({ commit, dispatch, state }, { ids, imageId }) => {
commit(types.SET_MAIN_LOADING, true);
const url = `/${state.config.projectPath}/registry/repository/${imageId}/tags/bulk_destroy`;
return axios
.delete(url, { params: { ids } })
.then(() => {
createFlash(DELETE_TAGS_SUCCESS_MESSAGE, 'success');
dispatch('requestTagsList', { pagination: state.tagsPagination, id: imageId });
})
.catch(() => {
createFlash(DELETE_TAGS_ERROR_MESSAGE);
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
export const requestDeleteImage = ({ commit, dispatch, state }, destroyPath) => {
commit(types.SET_MAIN_LOADING, true);
return axios
.delete(destroyPath)
.then(() => {
dispatch('requestImagesList', { pagination: state.pagination });
createFlash(DELETE_IMAGE_SUCCESS_MESSAGE, 'success');
})
.catch(() => {
createFlash(DELETE_IMAGE_ERROR_MESSAGE);
})
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import mutations from './mutations';
import state from './state';
Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
state,
actions,
mutations,
});
export default createStore();
export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
export const SET_IMAGES_LIST_SUCCESS = 'SET_PACKAGE_LIST_SUCCESS';
export const SET_PAGINATION = 'SET_PAGINATION';
export const SET_MAIN_LOADING = 'SET_MAIN_LOADING';
export const SET_TAGS_PAGINATION = 'SET_TAGS_PAGINATION';
export const SET_TAGS_LIST_SUCCESS = 'SET_TAGS_LIST_SUCCESS';
import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
export default {
[types.SET_INITIAL_STATE](state, config) {
state.config = {
...config,
};
},
[types.SET_IMAGES_LIST_SUCCESS](state, images) {
state.images = images;
},
[types.SET_TAGS_LIST_SUCCESS](state, tags) {
state.tags = tags;
},
[types.SET_MAIN_LOADING](state, isLoading) {
state.isLoading = isLoading;
},
[types.SET_PAGINATION](state, headers) {
const normalizedHeaders = normalizeHeaders(headers);
state.pagination = parseIntPagination(normalizedHeaders);
},
[types.SET_TAGS_PAGINATION](state, headers) {
const normalizedHeaders = normalizeHeaders(headers);
state.tagsPagination = parseIntPagination(normalizedHeaders);
},
};
export default () => ({
isLoading: false,
config: {},
images: [],
tags: [],
pagination: {},
tagsPagination: {},
});
...@@ -4,14 +4,20 @@ import Translate from '~/vue_shared/translate'; ...@@ -4,14 +4,20 @@ import Translate from '~/vue_shared/translate';
Vue.use(Translate); Vue.use(Translate);
export default () => export default () => {
new Vue({ const el = document.getElementById('js-vue-registry-images');
el: '#js-vue-registry-images',
if (!el) {
return null;
}
return new Vue({
el,
components: { components: {
registryApp, registryApp,
}, },
data() { data() {
const { dataset } = document.querySelector(this.$options.el); const { dataset } = el;
return { return {
registryData: { registryData: {
endpoint: dataset.endpoint, endpoint: dataset.endpoint,
...@@ -35,3 +41,4 @@ export default () => ...@@ -35,3 +41,4 @@ export default () =>
}); });
}, },
}); });
};
...@@ -3,10 +3,20 @@ ...@@ -3,10 +3,20 @@
%section %section
.row.registry-placeholder.prepend-bottom-10 .row.registry-placeholder.prepend-bottom-10
.col-12 .col-12
#js-vue-registry-images{ data: { endpoint: group_container_registries_path(@group, format: :json), - if Feature.enabled?(:vue_container_registry_explorer)
"help_page_path" => help_page_path('user/packages/container_registry/index'), #js-container-registry{ data: { endpoint: group_container_registries_path(@group),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'), "help_page_path" => help_page_path('user/packages/container_registry/index'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'), "two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'),
"repository_url" => "", "personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'),
is_group_page: true, "no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
character_error: @character_error.to_s } } "containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"registry_host_url_with_port" => escape_once(registry_config.host_port),
character_error: @character_error.to_s } }
- else
#js-vue-registry-images{ data: { endpoint: group_container_registries_path(@group, format: :json),
"help_page_path" => help_page_path('user/packages/container_registry/index'),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"repository_url" => "",
is_group_page: true,
character_error: @character_error.to_s } }
...@@ -3,12 +3,24 @@ ...@@ -3,12 +3,24 @@
%section %section
.row.registry-placeholder.prepend-bottom-10 .row.registry-placeholder.prepend-bottom-10
.col-12 .col-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json), - if Feature.enabled?(:vue_container_registry_explorer)
"help_page_path" => help_page_path('user/packages/container_registry/index'), #js-container-registry{ data: { endpoint: project_container_registry_index_path(@project),
"two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'), project_path: @project.full_path,
"personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'), "help_page_path" => help_page_path('user/packages/container_registry/index'),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'), "two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'), "personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'),
"repository_url" => escape_once(@project.container_registry_url), "no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"registry_host_url_with_port" => escape_once(registry_config.host_port), "containers_error_image" => image_path('illustrations/docker-error-state.svg'),
character_error: @character_error.to_s } } "repository_url" => escape_once(@project.container_registry_url),
"registry_host_url_with_port" => escape_once(registry_config.host_port),
character_error: @character_error.to_s } }
- else
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json),
"help_page_path" => help_page_path('user/packages/container_registry/index'),
"two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'),
"personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"repository_url" => escape_once(@project.container_registry_url),
"registry_host_url_with_port" => escape_once(registry_config.host_port),
character_error: @character_error.to_s } }
...@@ -10169,6 +10169,12 @@ msgstr "" ...@@ -10169,6 +10169,12 @@ msgstr ""
msgid "Image %{imageName} was scheduled for deletion from the registry." msgid "Image %{imageName} was scheduled for deletion from the registry."
msgstr "" msgstr ""
msgid "Image ID"
msgstr ""
msgid "Image deleted successfully"
msgstr ""
msgid "Image: %{image}" msgid "Image: %{image}"
msgstr "" msgstr ""
...@@ -11019,6 +11025,9 @@ msgstr "" ...@@ -11019,6 +11025,9 @@ msgstr ""
msgid "Last Seen" msgid "Last Seen"
msgstr "" msgstr ""
msgid "Last Updated"
msgstr ""
msgid "Last accessed on" msgid "Last accessed on"
msgstr "" msgstr ""
...@@ -17642,12 +17651,21 @@ msgstr "" ...@@ -17642,12 +17651,21 @@ msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later" msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "" msgstr ""
msgid "Something went wrong while deleting the image."
msgstr ""
msgid "Something went wrong while deleting the package." msgid "Something went wrong while deleting the package."
msgstr "" msgstr ""
msgid "Something went wrong while deleting the source branch. Please try again." msgid "Something went wrong while deleting the source branch. Please try again."
msgstr "" msgstr ""
msgid "Something went wrong while deleting the tag."
msgstr ""
msgid "Something went wrong while deleting the tags."
msgstr ""
msgid "Something went wrong while deleting your note. Please try again." msgid "Something went wrong while deleting your note. Please try again."
msgstr "" msgstr ""
...@@ -17690,6 +17708,9 @@ msgstr "" ...@@ -17690,6 +17708,9 @@ msgstr ""
msgid "Something went wrong while fetching the registry list." msgid "Something went wrong while fetching the registry list."
msgstr "" msgstr ""
msgid "Something went wrong while fetching the tags list."
msgstr ""
msgid "Something went wrong while initializing the OpenAPI viewer" msgid "Something went wrong while initializing the OpenAPI viewer"
msgstr "" msgstr ""
...@@ -18503,6 +18524,9 @@ msgstr "" ...@@ -18503,6 +18524,9 @@ msgstr ""
msgid "Tag" msgid "Tag"
msgstr "" msgstr ""
msgid "Tag deleted successfully"
msgstr ""
msgid "Tag list:" msgid "Tag list:"
msgstr "" msgstr ""
...@@ -18521,6 +18545,9 @@ msgstr "" ...@@ -18521,6 +18545,9 @@ msgstr ""
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
msgid "Tags deleted successfully"
msgstr ""
msgid "Tags feed" msgid "Tags feed"
msgstr "" msgstr ""
......
...@@ -15,6 +15,7 @@ describe 'Container Registry', :js do ...@@ -15,6 +15,7 @@ describe 'Container Registry', :js do
project.add_developer(user) project.add_developer(user)
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: :any, tags: []) stub_container_registry_tags(repository: :any, tags: [])
stub_feature_flags(vue_container_registry_explorer: false)
end end
it 'has a page title set' do it 'has a page title set' do
......
export const reposServerResponse = [
{
destroy_path: 'path',
id: '123',
location: 'location',
path: 'foo',
tags_path: 'tags_path',
},
{
destroy_path: 'path_',
id: '456',
location: 'location_',
path: 'bar',
tags_path: 'tags_path_',
},
];
export const registryServerResponse = [
{
name: 'centos7',
short_revision: 'b118ab5b0',
revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
total_size: 679,
layers: 19,
location: 'location',
created_at: 1505828744434,
destroy_path: 'path_',
},
{
name: 'centos6',
short_revision: 'b118ab5b0',
revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
total_size: 679,
layers: 19,
location: 'location',
created_at: 1505828744434,
},
];
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import * as actions from '~/registry/explorer/stores/actions';
import * as types from '~/registry/explorer/stores/mutation_types';
import testAction from 'helpers/vuex_action_helper';
import createFlash from '~/flash';
import { TEST_HOST } from 'helpers/test_constants';
import { reposServerResponse, registryServerResponse } from '../mock_data';
jest.mock('~/flash.js');
describe('Actions RegistryExplorer Store', () => {
let mock;
const endpoint = `${TEST_HOST}/endpoint.json`;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
it('sets initial state', done => {
const initialState = {
config: {
endpoint,
},
};
testAction(
actions.setInitialState,
initialState,
null,
[{ type: types.SET_INITIAL_STATE, payload: initialState }],
[],
done,
);
});
describe('receives api responses', () => {
const response = {
data: [1, 2, 3],
headers: {
page: 1,
perPage: 10,
},
};
it('images list response', done => {
testAction(
actions.receiveImagesListSuccess,
response,
null,
[
{ type: types.SET_IMAGES_LIST_SUCCESS, payload: response.data },
{ type: types.SET_PAGINATION, payload: response.headers },
],
[],
done,
);
});
it('tags list response', done => {
testAction(
actions.receiveTagsListSuccess,
response,
null,
[
{ type: types.SET_TAGS_LIST_SUCCESS, payload: response.data },
{ type: types.SET_TAGS_PAGINATION, payload: response.headers },
],
[],
done,
);
});
});
describe('fetch images list', () => {
it('sets the imagesList and pagination', done => {
mock.onGet(endpoint).replyOnce(200, reposServerResponse, {});
testAction(
actions.requestImagesList,
{},
{
config: {
endpoint,
},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[{ type: 'receiveImagesListSuccess', payload: { data: reposServerResponse, headers: {} } }],
done,
);
});
it('should create flash on error', done => {
testAction(
actions.requestImagesList,
{},
{
config: {
endpoint: null,
},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('fetch tags list', () => {
const url = window.btoa(`${endpoint}/1}`);
it('sets the tagsList', done => {
mock.onGet(window.atob(url)).replyOnce(200, registryServerResponse, {});
testAction(
actions.requestTagsList,
{ id: url },
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'receiveTagsListSuccess',
payload: { data: registryServerResponse, headers: {} },
},
],
done,
);
});
it('should create flash on error', done => {
testAction(
actions.requestTagsList,
{ id: url },
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('request delete single tag', () => {
it('successfully performs the delete request', done => {
const deletePath = 'delete/path';
const url = window.btoa(`${endpoint}/1}`);
mock.onDelete(deletePath).replyOnce(200);
testAction(
actions.requestDeleteTag,
{
tag: {
destroy_path: deletePath,
},
imageId: url,
},
{
tagsPagination: {},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'requestTagsList',
payload: { pagination: {}, id: url },
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
it('should show flash message on error', done => {
testAction(
actions.requestDeleteTag,
{
tag: {
destroy_path: null,
},
},
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('request delete multiple tags', () => {
const imageId = 1;
const projectPath = 'project-path';
const url = `${projectPath}/registry/repository/${imageId}/tags/bulk_destroy`;
it('successfully performs the delete request', done => {
mock.onDelete(url).replyOnce(200);
testAction(
actions.requestDeleteTags,
{
ids: [1, 2],
imageId,
},
{
config: {
projectPath,
},
tagsPagination: {},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'requestTagsList',
payload: { pagination: {}, id: 1 },
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
it('should show flash message on error', done => {
mock.onDelete(url).replyOnce(500);
testAction(
actions.requestDeleteTags,
{
ids: [1, 2],
imageId,
},
{
config: {
projectPath,
},
tagsPagination: {},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
describe('request delete single image', () => {
it('successfully performs the delete request', done => {
const deletePath = 'delete/path';
mock.onDelete(deletePath).replyOnce(200);
testAction(
actions.requestDeleteImage,
deletePath,
{
pagination: {},
},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'requestImagesList',
payload: { pagination: {} },
},
],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
it('should show flash message on error', done => {
testAction(
actions.requestDeleteImage,
null,
{},
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
() => {
expect(createFlash).toHaveBeenCalled();
done();
},
);
});
});
});
import mutations from '~/registry/explorer/stores/mutations';
import * as types from '~/registry/explorer/stores/mutation_types';
describe('Mutations Registry Explorer Store', () => {
let mockState;
beforeEach(() => {
mockState = {};
});
describe('SET_INITIAL_STATE', () => {
it('should set the initial state', () => {
const expectedState = { ...mockState, config: { endpoint: 'foo' } };
mutations[types.SET_INITIAL_STATE](mockState, { endpoint: 'foo' });
expect(mockState).toEqual(expectedState);
});
});
describe('SET_IMAGES_LIST_SUCCESS', () => {
it('should set the images list', () => {
const images = [1, 2, 3];
const expectedState = { ...mockState, images };
mutations[types.SET_IMAGES_LIST_SUCCESS](mockState, images);
expect(mockState).toEqual(expectedState);
});
});
describe('SET_TAGS_LIST_SUCCESS', () => {
it('should set the tags list', () => {
const tags = [1, 2, 3];
const expectedState = { ...mockState, tags };
mutations[types.SET_TAGS_LIST_SUCCESS](mockState, tags);
expect(mockState).toEqual(expectedState);
});
});
describe('SET_MAIN_LOADING', () => {
it('should set the isLoading', () => {
const expectedState = { ...mockState, isLoading: true };
mutations[types.SET_MAIN_LOADING](mockState, true);
expect(mockState).toEqual(expectedState);
});
});
describe('SET_PAGINATION', () => {
const generatePagination = () => [
{
'X-PAGE': '1',
'X-PER-PAGE': '20',
'X-TOTAL': '100',
'X-TOTAL-PAGES': '5',
'X-NEXT-PAGE': '2',
'X-PREV-PAGE': '0',
},
{
page: 1,
perPage: 20,
total: 100,
totalPages: 5,
nextPage: 2,
previousPage: 0,
},
];
it('should set the images pagination', () => {
const [headers, expectedResult] = generatePagination();
const expectedState = { ...mockState, pagination: expectedResult };
mutations[types.SET_PAGINATION](mockState, headers);
expect(mockState).toEqual(expectedState);
});
it('should set the tags pagination', () => {
const [headers, expectedResult] = generatePagination();
const expectedState = { ...mockState, tagsPagination: expectedResult };
mutations[types.SET_TAGS_PAGINATION](mockState, headers);
expect(mockState).toEqual(expectedState);
});
});
});
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