summaryrefslogtreecommitdiff
path: root/spec/frontend/registry
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-23 15:06:32 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-23 15:06:32 +0000
commit6f9edd1a4c4942d3d13ec54793cfae56164b1a0a (patch)
treef118f4a1dcad2db7b35ab15157e16eef56eba860 /spec/frontend/registry
parent94e614c94c0a42e261e6af88c89461d90f3330c0 (diff)
downloadgitlab-ce-6f9edd1a4c4942d3d13ec54793cfae56164b1a0a.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/registry')
-rw-r--r--spec/frontend/registry/components/app_spec.js121
-rw-r--r--spec/frontend/registry/components/collapsible_container_spec.js89
-rw-r--r--spec/frontend/registry/components/table_registry_spec.js211
-rw-r--r--spec/frontend/registry/mock_data.js134
-rw-r--r--spec/frontend/registry/stores/actions_spec.js189
-rw-r--r--spec/frontend/registry/stores/getters_spec.js (renamed from spec/frontend/registry/getters_spec.js)0
-rw-r--r--spec/frontend/registry/stores/mutations_spec.js85
7 files changed, 829 insertions, 0 deletions
diff --git a/spec/frontend/registry/components/app_spec.js b/spec/frontend/registry/components/app_spec.js
new file mode 100644
index 00000000000..190af5c11cd
--- /dev/null
+++ b/spec/frontend/registry/components/app_spec.js
@@ -0,0 +1,121 @@
+import registry from '~/registry/components/app.vue';
+import { mount } from '@vue/test-utils';
+import { TEST_HOST } from '../../helpers/test_constants';
+import { reposServerResponse, parsedReposServerResponse } from '../mock_data';
+
+describe('Registry List', () => {
+ let wrapper;
+
+ const findCollapsibleContainer = w => w.findAll({ name: 'CollapsibeContainerRegisty' });
+ const findNoContainerImagesText = w => w.find('.js-no-container-images-text');
+ const findSpinner = w => w.find('.gl-spinner');
+ const findCharacterErrorText = w => w.find('.js-character-error-text');
+
+ const propsData = {
+ endpoint: `${TEST_HOST}/foo`,
+ helpPagePath: 'foo',
+ noContainersImage: 'foo',
+ containersErrorImage: 'foo',
+ repositoryUrl: 'foo',
+ };
+
+ const setMainEndpoint = jest.fn();
+ const fetchRepos = jest.fn();
+
+ const methods = {
+ setMainEndpoint,
+ fetchRepos,
+ };
+
+ beforeEach(() => {
+ wrapper = mount(registry, {
+ propsData,
+ computed: {
+ repos() {
+ return parsedReposServerResponse;
+ },
+ },
+ methods,
+ });
+ });
+
+ describe('with data', () => {
+ it('should render a list of CollapsibeContainerRegisty', () => {
+ const containers = findCollapsibleContainer(wrapper);
+ expect(wrapper.vm.repos.length).toEqual(reposServerResponse.length);
+ expect(containers.length).toEqual(reposServerResponse.length);
+ });
+ });
+
+ describe('without data', () => {
+ let localWrapper;
+ beforeEach(() => {
+ localWrapper = mount(registry, {
+ propsData,
+ computed: {
+ repos() {
+ return [];
+ },
+ },
+ methods,
+ });
+ });
+
+ it('should render empty message', () => {
+ const noContainerImagesText = findNoContainerImagesText(localWrapper);
+ expect(noContainerImagesText.text()).toEqual(
+ 'With the Container Registry, every project can have its own space to store its Docker images. More Information',
+ );
+ });
+ });
+
+ describe('while loading data', () => {
+ let localWrapper;
+
+ beforeEach(() => {
+ localWrapper = mount(registry, {
+ propsData,
+ computed: {
+ repos() {
+ return [];
+ },
+ isLoading() {
+ return true;
+ },
+ },
+ methods,
+ });
+ });
+
+ it('should render a loading spinner', () => {
+ const spinner = findSpinner(localWrapper);
+ expect(spinner.exists()).toBe(true);
+ });
+ });
+
+ describe('invalid characters in path', () => {
+ let localWrapper;
+
+ beforeEach(() => {
+ localWrapper = mount(registry, {
+ propsData: {
+ ...propsData,
+ characterError: true,
+ },
+ computed: {
+ repos() {
+ return [];
+ },
+ },
+ methods,
+ });
+ });
+
+ it('should render invalid characters error message', () => {
+ const characterErrorText = findCharacterErrorText(localWrapper);
+ expect(characterErrorText.text()).toEqual(
+ 'We are having trouble connecting to Docker, which could be due to an issue with your project name or path. More Information',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/registry/components/collapsible_container_spec.js b/spec/frontend/registry/components/collapsible_container_spec.js
new file mode 100644
index 00000000000..0fe4338f1ba
--- /dev/null
+++ b/spec/frontend/registry/components/collapsible_container_spec.js
@@ -0,0 +1,89 @@
+import Vue from 'vue';
+import { mount } from '@vue/test-utils';
+import collapsibleComponent from '~/registry/components/collapsible_container.vue';
+import { repoPropsData } from '../mock_data';
+import createFlash from '~/flash';
+
+jest.mock('~/flash.js');
+
+describe('collapsible registry container', () => {
+ let wrapper;
+
+ const findDeleteBtn = w => w.find('.js-remove-repo');
+ const findContainerImageTags = w => w.find('.container-image-tags');
+ const findToggleRepos = w => w.findAll('.js-toggle-repo');
+
+ beforeEach(() => {
+ createFlash.mockClear();
+ // This is needed due to console.error called by vue to emit a warning that stop the tests
+ // see https://github.com/vuejs/vue-test-utils/issues/532
+ Vue.config.silent = true;
+ wrapper = mount(collapsibleComponent, {
+ propsData: {
+ repo: repoPropsData,
+ },
+ });
+ });
+
+ afterEach(() => {
+ Vue.config.silent = false;
+ });
+
+ describe('toggle', () => {
+ beforeEach(() => {
+ const fetchList = jest.fn();
+ wrapper.setMethods({ fetchList });
+ });
+
+ const expectIsClosed = () => {
+ const container = findContainerImageTags(wrapper);
+ expect(container.exists()).toBe(false);
+ expect(wrapper.vm.iconName).toEqual('angle-right');
+ };
+
+ it('should be closed by default', () => {
+ expectIsClosed();
+ });
+ it('should be open when user clicks on closed repo', () => {
+ const toggleRepos = findToggleRepos(wrapper);
+ toggleRepos.at(0).trigger('click');
+ const container = findContainerImageTags(wrapper);
+ expect(container.exists()).toBe(true);
+ expect(wrapper.vm.fetchList).toHaveBeenCalled();
+ });
+ it('should be closed when the user clicks on an opened repo', done => {
+ const toggleRepos = findToggleRepos(wrapper);
+ toggleRepos.at(0).trigger('click');
+ Vue.nextTick(() => {
+ toggleRepos.at(0).trigger('click');
+ Vue.nextTick(() => {
+ expectIsClosed();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('delete repo', () => {
+ it('should be possible to delete a repo', () => {
+ const deleteBtn = findDeleteBtn(wrapper);
+ expect(deleteBtn.exists()).toBe(true);
+ });
+
+ it('should call deleteItem when confirming deletion', () => {
+ const deleteItem = jest.fn().mockResolvedValue();
+ const fetchRepos = jest.fn().mockResolvedValue();
+ wrapper.setMethods({ deleteItem, fetchRepos });
+ wrapper.vm.handleDeleteRepository();
+ expect(wrapper.vm.deleteItem).toHaveBeenCalledWith(wrapper.vm.repo);
+ });
+
+ it('should show an error when there is API error', () => {
+ const deleteItem = jest.fn().mockRejectedValue('error');
+ wrapper.setMethods({ deleteItem });
+ return wrapper.vm.handleDeleteRepository().then(() => {
+ expect(createFlash).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/registry/components/table_registry_spec.js b/spec/frontend/registry/components/table_registry_spec.js
new file mode 100644
index 00000000000..a2eee4aada7
--- /dev/null
+++ b/spec/frontend/registry/components/table_registry_spec.js
@@ -0,0 +1,211 @@
+import Vue from 'vue';
+import tableRegistry from '~/registry/components/table_registry.vue';
+import { mount } from '@vue/test-utils';
+import { repoPropsData } from '../mock_data';
+
+const [firstImage, secondImage] = repoPropsData.list;
+
+describe('table registry', () => {
+ let wrapper;
+
+ const findSelectAllCheckbox = w => w.find('.js-select-all-checkbox > input');
+ const findSelectCheckboxes = w => w.findAll('.js-select-checkbox > input');
+ const findDeleteButton = w => w.find('.js-delete-registry');
+ const findDeleteButtonsRow = w => w.findAll('.js-delete-registry-row');
+ const findPagination = w => w.find('.js-registry-pagination');
+ const bulkDeletePath = 'path';
+
+ beforeEach(() => {
+ // This is needed due to console.error called by vue to emit a warning that stop the tests
+ // see https://github.com/vuejs/vue-test-utils/issues/532
+ Vue.config.silent = true;
+ wrapper = mount(tableRegistry, {
+ propsData: {
+ repo: repoPropsData,
+ },
+ });
+ });
+
+ afterEach(() => {
+ Vue.config.silent = false;
+ });
+
+ describe('rendering', () => {
+ it('should render a table with the registry list', () => {
+ expect(wrapper.findAll('.registry-image-row').length).toEqual(repoPropsData.list.length);
+ });
+
+ it('should render registry tag', () => {
+ const tds = wrapper.findAll('.registry-image-row td');
+ expect(tds.at(0).classes()).toContain('check');
+ expect(tds.at(1).html()).toContain(repoPropsData.list[0].tag);
+ expect(tds.at(2).html()).toContain(repoPropsData.list[0].shortRevision);
+ expect(tds.at(3).html()).toContain(repoPropsData.list[0].layers);
+ expect(tds.at(3).html()).toContain(repoPropsData.list[0].size);
+ expect(tds.at(4).html()).toContain(wrapper.vm.timeFormated(repoPropsData.list[0].createdAt));
+ });
+ });
+
+ describe('multi select', () => {
+ it('selecting a row should enable delete button', done => {
+ const deleteBtn = findDeleteButton(wrapper);
+ const checkboxes = findSelectCheckboxes(wrapper);
+
+ expect(deleteBtn.attributes('disabled')).toBe('disabled');
+
+ checkboxes.at(0).trigger('click');
+ Vue.nextTick(() => {
+ expect(deleteBtn.attributes('disabled')).toEqual(undefined);
+ done();
+ });
+ });
+
+ it('selecting all checkbox should select all rows and enable delete button', done => {
+ const selectAll = findSelectAllCheckbox(wrapper);
+ const checkboxes = findSelectCheckboxes(wrapper);
+ selectAll.trigger('click');
+
+ Vue.nextTick(() => {
+ const checked = checkboxes.filter(w => w.element.checked);
+ expect(checked.length).toBe(checkboxes.length);
+ done();
+ });
+ });
+
+ it('deselecting select all checkbox should deselect all rows and disable delete button', done => {
+ const checkboxes = findSelectCheckboxes(wrapper);
+ const selectAll = findSelectAllCheckbox(wrapper);
+ selectAll.trigger('click');
+ selectAll.trigger('click');
+
+ Vue.nextTick(() => {
+ const checked = checkboxes.filter(w => !w.element.checked);
+ expect(checked.length).toBe(checkboxes.length);
+ done();
+ });
+ });
+
+ it('should delete multiple items when multiple items are selected', done => {
+ const multiDeleteItems = jest.fn().mockResolvedValue();
+ wrapper.setMethods({ multiDeleteItems });
+ const selectAll = findSelectAllCheckbox(wrapper);
+ selectAll.trigger('click');
+
+ Vue.nextTick(() => {
+ const deleteBtn = findDeleteButton(wrapper);
+ expect(wrapper.vm.itemsToBeDeleted).toEqual([0, 1]);
+ expect(deleteBtn.attributes('disabled')).toEqual(undefined);
+ wrapper.vm.handleMultipleDelete();
+
+ Vue.nextTick(() => {
+ expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
+ expect(wrapper.vm.multiDeleteItems).toHaveBeenCalledWith({
+ path: bulkDeletePath,
+ items: [firstImage.tag, secondImage.tag],
+ });
+ done();
+ });
+ });
+ });
+
+ it('should show an error message if bulkDeletePath is not set', () => {
+ const showError = jest.fn();
+ wrapper.setMethods({ showError });
+ wrapper.setProps({
+ repo: {
+ ...repoPropsData,
+ tagsPath: null,
+ },
+ });
+ wrapper.vm.handleMultipleDelete();
+ expect(wrapper.vm.showError).toHaveBeenCalled();
+ });
+ });
+
+ describe('delete registry', () => {
+ beforeEach(() => {
+ wrapper.setData({ itemsToBeDeleted: [0] });
+ });
+
+ it('should be possible to delete a registry', () => {
+ const deleteBtn = findDeleteButton(wrapper);
+ const deleteBtns = findDeleteButtonsRow(wrapper);
+ expect(wrapper.vm.itemsToBeDeleted).toEqual([0]);
+ expect(deleteBtn).toBeDefined();
+ expect(deleteBtn.attributes('disable')).toBe(undefined);
+ expect(deleteBtns.is('button')).toBe(true);
+ });
+
+ it('should allow deletion row by row', () => {
+ const deleteBtns = findDeleteButtonsRow(wrapper);
+ const deleteSingleItem = jest.fn();
+ const deleteItem = jest.fn().mockResolvedValue();
+ wrapper.setMethods({ deleteSingleItem, deleteItem });
+ deleteBtns.at(0).trigger('click');
+ expect(wrapper.vm.deleteSingleItem).toHaveBeenCalledWith(0);
+ wrapper.vm.handleSingleDelete(1);
+ expect(wrapper.vm.deleteItem).toHaveBeenCalledWith(1);
+ });
+ });
+
+ describe('pagination', () => {
+ let localWrapper = null;
+ const repo = {
+ repoPropsData,
+ pagination: {
+ total: 20,
+ perPage: 2,
+ nextPage: 2,
+ },
+ };
+
+ beforeEach(() => {
+ localWrapper = mount(tableRegistry, {
+ propsData: {
+ repo,
+ },
+ });
+ });
+
+ it('should exist', () => {
+ const pagination = findPagination(localWrapper);
+ expect(pagination.exists()).toBe(true);
+ });
+ it('should be visible when pagination is needed', () => {
+ const pagination = findPagination(localWrapper);
+ expect(pagination.isVisible()).toBe(true);
+ localWrapper.setProps({
+ repo: {
+ pagination: {
+ total: 0,
+ perPage: 10,
+ },
+ },
+ });
+ expect(localWrapper.vm.shouldRenderPagination).toBe(false);
+ });
+ it('should have a change function that update the list when run', () => {
+ const fetchList = jest.fn().mockResolvedValue();
+ localWrapper.setMethods({ fetchList });
+ localWrapper.vm.onPageChange(1);
+ expect(localWrapper.vm.fetchList).toHaveBeenCalledWith({ repo, page: 1 });
+ });
+ });
+
+ describe('modal content', () => {
+ it('should show the singular title and image name when deleting a single image', () => {
+ wrapper.setData({ itemsToBeDeleted: [1] });
+ wrapper.vm.setModalDescription(0);
+ expect(wrapper.vm.modalTitle).toBe('Remove image');
+ expect(wrapper.vm.modalDescription).toContain(firstImage.tag);
+ });
+
+ it('should show the plural title and image count when deleting more than one image', () => {
+ wrapper.setData({ itemsToBeDeleted: [1, 2] });
+ wrapper.vm.setModalDescription();
+
+ expect(wrapper.vm.modalTitle).toBe('Remove images');
+ expect(wrapper.vm.modalDescription).toContain('<b>2</b> images');
+ });
+ });
+});
diff --git a/spec/frontend/registry/mock_data.js b/spec/frontend/registry/mock_data.js
new file mode 100644
index 00000000000..130ab298e89
--- /dev/null
+++ b/spec/frontend/registry/mock_data.js
@@ -0,0 +1,134 @@
+export const defaultState = {
+ isLoading: false,
+ endpoint: '',
+ repos: [],
+};
+
+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,
+ },
+];
+
+export const parsedReposServerResponse = [
+ {
+ canDelete: true,
+ destroyPath: reposServerResponse[0].destroy_path,
+ id: reposServerResponse[0].id,
+ isLoading: false,
+ list: [],
+ location: reposServerResponse[0].location,
+ name: reposServerResponse[0].path,
+ tagsPath: reposServerResponse[0].tags_path,
+ },
+ {
+ canDelete: true,
+ destroyPath: reposServerResponse[1].destroy_path,
+ id: reposServerResponse[1].id,
+ isLoading: false,
+ list: [],
+ location: reposServerResponse[1].location,
+ name: reposServerResponse[1].path,
+ tagsPath: reposServerResponse[1].tags_path,
+ },
+];
+
+export const parsedRegistryServerResponse = [
+ {
+ tag: registryServerResponse[0].name,
+ revision: registryServerResponse[0].revision,
+ shortRevision: registryServerResponse[0].short_revision,
+ size: registryServerResponse[0].total_size,
+ layers: registryServerResponse[0].layers,
+ location: registryServerResponse[0].location,
+ createdAt: registryServerResponse[0].created_at,
+ destroyPath: registryServerResponse[0].destroy_path,
+ canDelete: true,
+ },
+ {
+ tag: registryServerResponse[1].name,
+ revision: registryServerResponse[1].revision,
+ shortRevision: registryServerResponse[1].short_revision,
+ size: registryServerResponse[1].total_size,
+ layers: registryServerResponse[1].layers,
+ location: registryServerResponse[1].location,
+ createdAt: registryServerResponse[1].created_at,
+ destroyPath: registryServerResponse[1].destroy_path,
+ canDelete: false,
+ },
+];
+
+export const repoPropsData = {
+ canDelete: true,
+ destroyPath: 'path',
+ id: '123',
+ isLoading: false,
+ list: [
+ {
+ tag: 'centos6',
+ revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43',
+ shortRevision: 'b118ab5b0',
+ size: 19,
+ layers: 10,
+ location: 'location',
+ createdAt: 1505828744434,
+ destroyPath: 'path',
+ canDelete: true,
+ },
+ {
+ tag: 'test-image',
+ revision: 'b969de599faea2b3d9b6605a8b0897261c571acaa36db1bdc7349b5775b4e0b4',
+ shortRevision: 'b969de599',
+ size: 19,
+ layers: 10,
+ location: 'location-2',
+ createdAt: 1505828744434,
+ destroyPath: 'path-2',
+ canDelete: true,
+ },
+ ],
+ location: 'location',
+ name: 'foo',
+ tagsPath: 'path',
+ pagination: {
+ perPage: 5,
+ page: 1,
+ total: 13,
+ totalPages: 1,
+ nextPage: null,
+ previousPage: null,
+ },
+};
diff --git a/spec/frontend/registry/stores/actions_spec.js b/spec/frontend/registry/stores/actions_spec.js
new file mode 100644
index 00000000000..bf335904d23
--- /dev/null
+++ b/spec/frontend/registry/stores/actions_spec.js
@@ -0,0 +1,189 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import * as actions from '~/registry/stores/actions';
+import * as types from '~/registry/stores/mutation_types';
+import { TEST_HOST } from '../../helpers/test_constants';
+import testAction from '../../helpers/vuex_action_helper';
+import createFlash from '~/flash';
+
+import {
+ reposServerResponse,
+ registryServerResponse,
+ parsedReposServerResponse,
+} from '../mock_data';
+
+jest.mock('~/flash.js');
+
+describe('Actions Registry Store', () => {
+ let mock;
+ let state;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state = {
+ endpoint: `${TEST_HOST}/endpoint.json`,
+ };
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('fetchRepos', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, reposServerResponse, {});
+ });
+
+ it('should set receveived repos', done => {
+ testAction(
+ actions.fetchRepos,
+ null,
+ state,
+ [
+ { type: types.TOGGLE_MAIN_LOADING },
+ { type: types.TOGGLE_MAIN_LOADING },
+ { type: types.SET_REPOS_LIST, payload: reposServerResponse },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('should create flash on API error', done => {
+ testAction(
+ actions.fetchRepos,
+ null,
+ {
+ endpoint: null,
+ },
+ [{ type: types.TOGGLE_MAIN_LOADING }, { type: types.TOGGLE_MAIN_LOADING }],
+ [],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ done();
+ },
+ );
+ });
+ });
+
+ describe('fetchList', () => {
+ let repo;
+ beforeEach(() => {
+ state.repos = parsedReposServerResponse;
+ [, repo] = state.repos;
+ mock.onGet(repo.tagsPath).replyOnce(200, registryServerResponse, {});
+ });
+
+ it('should set received list', done => {
+ testAction(
+ actions.fetchList,
+ { repo },
+ state,
+ [
+ { type: types.TOGGLE_REGISTRY_LIST_LOADING, payload: repo },
+ { type: types.TOGGLE_REGISTRY_LIST_LOADING, payload: repo },
+ {
+ type: types.SET_REGISTRY_LIST,
+ payload: {
+ repo,
+ resp: registryServerResponse,
+ headers: expect.anything(),
+ },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('should create flash on API error', done => {
+ const updatedRepo = {
+ ...repo,
+ tagsPath: null,
+ };
+ testAction(
+ actions.fetchList,
+ {
+ repo: updatedRepo,
+ },
+ state,
+ [
+ { type: types.TOGGLE_REGISTRY_LIST_LOADING, payload: updatedRepo },
+ { type: types.TOGGLE_REGISTRY_LIST_LOADING, payload: updatedRepo },
+ ],
+ [],
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ done();
+ },
+ );
+ });
+ });
+
+ describe('setMainEndpoint', () => {
+ it('should commit set main endpoint', done => {
+ testAction(
+ actions.setMainEndpoint,
+ 'endpoint',
+ state,
+ [{ type: types.SET_MAIN_ENDPOINT, payload: 'endpoint' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('toggleLoading', () => {
+ it('should commit toggle main loading', done => {
+ testAction(
+ actions.toggleLoading,
+ null,
+ state,
+ [{ type: types.TOGGLE_MAIN_LOADING }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('deleteItem and multiDeleteItems', () => {
+ let deleted;
+ const destroyPath = `${TEST_HOST}/mygroup/myproject/container_registry/1.json`;
+
+ const expectDelete = done => {
+ expect(mock.history.delete.length).toBe(1);
+ expect(deleted).toBe(true);
+ done();
+ };
+
+ beforeEach(() => {
+ deleted = false;
+ mock.onDelete(destroyPath).replyOnce(() => {
+ deleted = true;
+ return [200];
+ });
+ });
+
+ it('deleteItem should perform DELETE request on destroyPath', done => {
+ testAction(
+ actions.deleteItem,
+ {
+ destroyPath,
+ },
+ state,
+ )
+ .then(() => {
+ expectDelete(done);
+ })
+ .catch(done.fail);
+ });
+
+ it('multiDeleteItems should perform DELETE request on path', done => {
+ testAction(actions.multiDeleteItems, { path: destroyPath, items: [1] }, state)
+ .then(() => {
+ expectDelete(done);
+ })
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/frontend/registry/getters_spec.js b/spec/frontend/registry/stores/getters_spec.js
index 839aa718997..839aa718997 100644
--- a/spec/frontend/registry/getters_spec.js
+++ b/spec/frontend/registry/stores/getters_spec.js
diff --git a/spec/frontend/registry/stores/mutations_spec.js b/spec/frontend/registry/stores/mutations_spec.js
new file mode 100644
index 00000000000..e19fe7a27cf
--- /dev/null
+++ b/spec/frontend/registry/stores/mutations_spec.js
@@ -0,0 +1,85 @@
+import mutations from '~/registry/stores/mutations';
+import * as types from '~/registry/stores/mutation_types';
+import {
+ defaultState,
+ reposServerResponse,
+ registryServerResponse,
+ parsedReposServerResponse,
+ parsedRegistryServerResponse,
+} from '../mock_data';
+
+describe('Mutations Registry Store', () => {
+ let mockState;
+ beforeEach(() => {
+ mockState = defaultState;
+ });
+
+ describe('SET_MAIN_ENDPOINT', () => {
+ it('should set the main endpoint', () => {
+ const expectedState = Object.assign({}, mockState, { endpoint: 'foo' });
+ mutations[types.SET_MAIN_ENDPOINT](mockState, 'foo');
+
+ expect(mockState).toEqual(expectedState);
+ });
+ });
+
+ describe('SET_REPOS_LIST', () => {
+ it('should set a parsed repository list', () => {
+ mutations[types.SET_REPOS_LIST](mockState, reposServerResponse);
+
+ expect(mockState.repos).toEqual(parsedReposServerResponse);
+ });
+ });
+
+ describe('TOGGLE_MAIN_LOADING', () => {
+ it('should set a parsed repository list', () => {
+ mutations[types.TOGGLE_MAIN_LOADING](mockState);
+
+ expect(mockState.isLoading).toEqual(true);
+ });
+ });
+
+ describe('SET_REGISTRY_LIST', () => {
+ it('should set a list of registries in a specific repository', () => {
+ mutations[types.SET_REPOS_LIST](mockState, reposServerResponse);
+ mutations[types.SET_REGISTRY_LIST](mockState, {
+ repo: mockState.repos[0],
+ resp: registryServerResponse,
+ headers: {
+ 'x-per-page': 2,
+ 'x-page': 1,
+ 'x-total': 10,
+ },
+ });
+
+ expect(mockState.repos[0].list).toEqual(parsedRegistryServerResponse);
+ expect(mockState.repos[0].pagination).toEqual({
+ perPage: 2,
+ page: 1,
+ total: 10,
+ totalPages: NaN,
+ nextPage: NaN,
+ previousPage: NaN,
+ });
+ });
+ });
+
+ describe('TOGGLE_REGISTRY_LIST_LOADING', () => {
+ it('should toggle isLoading property for a specific repository', () => {
+ mutations[types.SET_REPOS_LIST](mockState, reposServerResponse);
+ mutations[types.SET_REGISTRY_LIST](mockState, {
+ repo: mockState.repos[0],
+ resp: registryServerResponse,
+ headers: {
+ 'x-per-page': 2,
+ 'x-page': 1,
+ 'x-total': 10,
+ },
+ });
+
+ mutations[types.TOGGLE_REGISTRY_LIST_LOADING](mockState, mockState.repos[0]);
+
+ expect(mockState.repos[0].isLoading).toEqual(true);
+ });
+ });
+});