diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-09-20 19:03:53 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-09-20 19:03:53 +0100 |
commit | ee3cf5d6f3d5a3631fa7e94a242f2dfe9b38a935 (patch) | |
tree | 855937f227caaac46688dca63f82a18598be778b | |
parent | 6c63520ef5735e56749c77b495f8137a20942504 (diff) | |
download | gitlab-ce-ee3cf5d6f3d5a3631fa7e94a242f2dfe9b38a935.tar.gz |
[ci skip] Adds tests to vuex and collapsibe component
Formats dates
Fixes clipboard button
Simplifies HTML
19 files changed, 469 insertions, 95 deletions
diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue index 17a57ae248d..c4d66382850 100644 --- a/app/assets/javascripts/registry/components/app.vue +++ b/app/assets/javascripts/registry/components/app.vue @@ -33,7 +33,7 @@ 'fetchList', 'deleteRepo', 'deleteRegistry', - 'toggleIsLoading', + 'toggleLoading', ]), fetchRegistryList(repo) { @@ -49,7 +49,7 @@ deleteRepository(repo) { this.deleteRepo(repo) - .then(() => this.fetchRepo()) + .then(() => this.fetchRepos()) .catch(() => this.showError(errorMessagesTypes.DELETE_REPO)); }, diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue index 6be2aa60ebd..739e48b93f2 100644 --- a/app/assets/javascripts/registry/components/collapsible_container.vue +++ b/app/assets/javascripts/registry/components/collapsible_container.vue @@ -2,6 +2,7 @@ import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import tooltip from '../../vue_shared/directives/tooltip'; + import timeagoMixin from '../../vue_shared/mixins/timeago'; export default { name: 'collapsibeContainerRegisty', @@ -15,6 +16,9 @@ clipboardButton, loadingIcon, }, + mixins: [ + timeagoMixin, + ], directives: { tooltip, }, @@ -28,16 +32,18 @@ const pluralize = gl.text.pluralize('layer', item.layers); return `${item.layers} ${pluralize}`; }, + toggleRepo() { if (this.isOpen === false) { - // consider not fetching data the second time it is toggled? :fry: this.$emit('fetchRegistryList', this.repo); } this.isOpen = !this.isOpen; }, + handleDeleteRepository() { this.$emit('deleteRepository', this.repo) }, + handleDeleteRegistry(registry) { this.$emit('deleteRegistry', this.repo, registry); }, @@ -51,7 +57,8 @@ class="container-image-head"> <a role="button" - @click="toggleRepo"> + @click="toggleRepo" + class="js-toggle-repo"> <i class="fa" :class="{ @@ -63,13 +70,17 @@ {{repo.name}} </a> - <clipboard-button text="foo" title="bar" /> + <clipboard-button + v-if="repo.location" + :text="__(`docker pull ${repo.location}`)" + :title="repo.location" + /> <div class="controls hidden-xs pull-right"> <button v-if="repo.canDelete" type="button" - class="btn btn-remove" + class="js-remove-repo btn btn-remove" :title="__('Remove repository')" v-tooltip @click="handleDeleteRepository"> @@ -90,14 +101,16 @@ v-else-if="!repo.isLoading && isOpen" class="container-image-tags"> - <table class="table tags" v-if="repo.list.length"> + <table + class="table tags" + v-if="repo.list.length"> <thead> <tr> <th>{{__("Tag")}}</th> <th>{{__("Tag ID")}}</th> <th>{{__("Size")}}</th> <th>{{__("Created")}}</th> - <th v-if="true"></th> + <th></th> </tr> </thead> <tbody> @@ -109,16 +122,16 @@ {{item.tag}} <clipboard-button - :title="item.tag" - :text="item.tag" + v-if="item.location" + :title="item.location" + :text="__(`docker pull ${item.location}`)" /> </td> <td> <span v-tooltip :title="item.revision" - data-placement="bottom" - > + data-placement="bottom"> {{item.shortRevision}} </span> </td> @@ -128,34 +141,38 @@ · {{layers(item)}} </template> - <div v-else class="light"> + <div + v-else + class="light"> \- </div> </td> <td> <template v-if="item.createdAt"> - format {{item.createdAt}} + {{timeFormated(item.createdAt)}} </template> - <div v-else class="light"> + <div + v-else + class="light"> \- </div> </td> <td class="content"> - <div class="controls hidden-xs pull-right"> - <button - type="button" - class="btn btn-remove" - title="Remove tag" - v-tooltip - @click="handleDeleteRegistry(item)"> - <i - class="fa fa-trash" - aria-hidden="true"> - </i> - </button> - </div> + <button + v-if="item.canDelete" + type="button" + class="js-delete-registry btn btn-remove hidden-xs pull-right" + :title="__('Remove tag')" + data-container="body" + v-tooltip + @click="handleDeleteRegistry(item)"> + <i + class="fa fa-trash" + aria-hidden="true"> + </i> + </button> </td> </tr> </tbody> diff --git a/app/assets/javascripts/registry/index.js b/app/assets/javascripts/registry/index.js index 4f7895897b2..ad76dfd333b 100644 --- a/app/assets/javascripts/registry/index.js +++ b/app/assets/javascripts/registry/index.js @@ -1,9 +1,6 @@ import Vue from 'vue'; -import Translate from '../vue_shared/translate'; import registryApp from './components/app.vue'; -// Vue.use(Translate); - document.addEventListener('DOMContentLoaded', () => new Vue({ el: '#js-vue-registry-images', components: { diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js index 5dda16b8d9a..c86e40a1a28 100644 --- a/app/assets/javascripts/registry/stores/actions.js +++ b/app/assets/javascripts/registry/stores/actions.js @@ -27,16 +27,10 @@ export const fetchList = ({ commit }, list) => { }; export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.path) - .then(res => res.json()) - .then(() => { - commit(types.DELETE_REPO, repo); - }); + .then(res => res.json()); export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.path) - .then(res => res.json()) - .then(() => { - commit(types.DELETE_IMAGE, image); - }); + .then(res => res.json()); export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); -export const toggleIsLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING); +export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING); diff --git a/app/assets/javascripts/registry/stores/getters.js b/app/assets/javascripts/registry/stores/getters.js index 6c6ed0cd738..588f479c492 100644 --- a/app/assets/javascripts/registry/stores/getters.js +++ b/app/assets/javascripts/registry/stores/getters.js @@ -1,2 +1,2 @@ export const isLoading = state => state.isLoading; -export const repos = state => state.repos;
\ No newline at end of file +export const repos = state => state.repos; diff --git a/app/assets/javascripts/registry/stores/mutation_types.js b/app/assets/javascripts/registry/stores/mutation_types.js index aece401a24a..2c69bf11807 100644 --- a/app/assets/javascripts/registry/stores/mutation_types.js +++ b/app/assets/javascripts/registry/stores/mutation_types.js @@ -1,10 +1,7 @@ export const SET_MAIN_ENDPOINT = 'SET_MAIN_ENDPOINT'; -export const FETCH_REPOS_LIST = 'FETCH_REPOS_LIST'; -export const DELETE_REPO = 'DELETE_REPO'; + export const SET_REPOS_LIST = 'SET_REPOS_LIST'; export const TOGGLE_MAIN_LOADING = 'TOGGLE_MAIN_LOADING'; -export const FETCH_IMAGES_LIST = 'FETCH_IMAGES_LIST'; export const SET_REGISTRY_LIST = 'SET_REGISTRY_LIST'; -export const DELETE_IMAGE = 'DELETE_IMAGE'; export const TOGGLE_REGISTRY_LIST_LOADING = 'TOGGLE_REGISTRY_LIST_LOADING'; diff --git a/app/assets/javascripts/registry/stores/mutations.js b/app/assets/javascripts/registry/stores/mutations.js index 796548bffec..0e69d2bed1b 100644 --- a/app/assets/javascripts/registry/stores/mutations.js +++ b/app/assets/javascripts/registry/stores/mutations.js @@ -11,12 +11,12 @@ export default { repos: list.map(el => ({ canDelete: !!el.destroy_path, destroyPath: el.destroy_path, + id: el.id, isLoading: false, list: [], location: el.location, name: el.name, tagsPath: el.tags_path, - id: el.id, })), }); }, @@ -26,26 +26,6 @@ export default { }, [types.SET_REGISTRY_LIST](state, repo, list) { - // mock - list = [ - { - name: 'centos6', - short_revision: '0b6091a66', - revision: '0b6091a665af68bbbbb36a3e088ec3cd6f35389deebf6d4617042d56722d76fb', - size: 706, - layers: 19, - created_at: 1505828744434, - }, - { - name: 'centos7', - short_revision: 'b118ab5b0', - revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43', - size: 679, - layers: 19, - created_at: 1505828744434, - }, - ]; - const listToUpdate = state.repos.find(el => el.id === repo.id); listToUpdate.list = list.map(element => ({ @@ -54,6 +34,7 @@ export default { shortRevision: element.short_revision, size: element.size, layers: element.layers, + location: element.location, createdAt: element.created_at, destroyPath: element.destroy_path, canDelete: !!element.destroy_path, diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index fbf7233b13d..3a7143c450e 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -1,5 +1,7 @@ <script> - import Clipboard from 'vendor/clipboard'; + /** + * Falls back to the code used in `copy_to_clipboard.js` + */ export default { name: 'clipboardButton', @@ -13,13 +15,6 @@ required: true, }, }, - mounted() { - // return new Clipboard(this.$refs.btn, { - // text: () => { - // return this.text; - // }, - // }); - } }; </script> @@ -28,9 +23,7 @@ type="button" class="btn btn-transparent btn-clipboard" :data-title="title" - :data-clipboard-text="text" - ref="btn" - > + :data-clipboard-text="text"> <i aria-hidden="true" class="fa fa-clipboard"> diff --git a/app/assets/stylesheets/pages/container_registry.scss b/app/assets/stylesheets/pages/container_registry.scss index 089a693efe4..3266714396e 100644 --- a/app/assets/stylesheets/pages/container_registry.scss +++ b/app/assets/stylesheets/pages/container_registry.scss @@ -9,10 +9,6 @@ .container-image-head { padding: 0 16px; line-height: 4em; - - &:hover { - text-decoration: underline; - } } .table.tags { diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index 89093e4172a..952081a349f 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -10,7 +10,7 @@ module Projects respond_to do |format| format.html format.json do - # render json: @images + # Remove code below render json: [ { name: 'gitlab-org/omnibus-gitlab/foo', @@ -41,13 +41,27 @@ module Projects def destroy if image.destroy - redirect_to project_container_registry_index_path(@project), - status: 302, - notice: 'Image repository has been removed successfully!' + respond_to do |format| + # TODO: @Kamil, I don't think this is used ever. Should we keep it or remove it? + format.html do + redirect_to project_container_registry_index_path(@project), + status: 302, + notice: 'Image repository has been removed successfully!' + end + + format.json { head :no_content } + end else - redirect_to project_container_registry_index_path(@project), - status: 302, - alert: 'Failed to remove image repository!' + respond_to do |format| + # TODO: @Kamil, I don't think this is used ever. Should we keep it or remove it? + format.html do + redirect_to project_container_registry_index_path(@project), + status: 302, + alert: 'Failed to remove image repository!' + end + + format.json { head :no_content } + end end end diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index 4a76431494c..9bf5eb03cb0 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -8,7 +8,7 @@ = _('With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images.') %p.append-bottom-0 = succeed '.' do - Learn more about + = _('Learn more about') = link_to _('Container Registry'), help_page_path('user/project/container_registry'), target: '_blank' .row .col-lg-12 @@ -20,14 +20,14 @@ %p = _('First log in to GitLab’s Container Registry using your GitLab username and password. If you have') = link_to _('2FA enabled'), help_page_path('user/profile/account/two_factor_authentication'), target: '_blank' - you need to use a + = _('you need to use a') = succeed ':' do = link_to _('personal access token'), help_page_path('user/profile/account/two_factor_authentication', anchor: 'personal-access-tokens'), target: '_blank' %pre docker login #{Gitlab.config.registry.host_port} %br %p - = _("Once you log in, you’re free to create and upload a container image using the common") + = _('Once you log in, you’re free to create and upload a container image using the common') %code = _('build') = _('and') @@ -37,7 +37,6 @@ :plain docker build -t #{escape_once(@project.container_registry_url)} . docker push #{escape_once(@project.container_registry_url)} - %hr %h5.prepend-top-default = _('Use different image names') @@ -48,8 +47,6 @@ #{escape_once(@project.container_registry_url)}:tag #{escape_once(@project.container_registry_url)}/optional-image-name:tag #{escape_once(@project.container_registry_url)}/optional-name/optional-image-name:tag - - .row .col-lg-12 #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json)}} diff --git a/spec/javascripts/notes/stores/helpers.js b/spec/javascripts/helpers/vuex_action_helper.js index 2d386fe1da5..2d386fe1da5 100644 --- a/spec/javascripts/notes/stores/helpers.js +++ b/spec/javascripts/helpers/vuex_action_helper.js diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js index 72d362acb2f..4359899299f 100644 --- a/spec/javascripts/notes/stores/actions_spec.js +++ b/spec/javascripts/notes/stores/actions_spec.js @@ -1,6 +1,6 @@ import * as actions from '~/notes/stores/actions'; -import testAction from './helpers'; +import testAction from '../../helpers/vuex_action_helper'; import { discussionMock, notesDataMock, userDataMock, issueDataMock, individualNote } from '../mock_data'; describe('Actions Notes Store', () => { diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/spec/javascripts/registry/components/app_spec.js diff --git a/spec/javascripts/registry/components/collapsible_container_spec.js b/spec/javascripts/registry/components/collapsible_container_spec.js new file mode 100644 index 00000000000..b9372f48965 --- /dev/null +++ b/spec/javascripts/registry/components/collapsible_container_spec.js @@ -0,0 +1,112 @@ +import Vue from 'vue'; +import collapsibleComponent from '~/registry/components/collapsible_container.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +describe('collapsible registry container', () => { + let vm; + let Component; + let mockData; + + beforeEach(() => { + Component = Vue.extend(collapsibleComponent); + mockData = { + 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, + }, + ], + location: 'location', + name: 'foo', + tagsPath: 'path', + }; + vm = mountComponent(Component, { repo: mockData }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('toggle', () => { + it('should be closed by default', () => { + expect(vm.$el.querySelector('.container-image-tags')).toBe(null); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right'); + }); + + it('should be open when user clicks on closed repo', (done) => { + vm.$el.querySelector('.js-toggle-repo').click(); + Vue.nextTick(() => { + expect(vm.$el.querySelector('.container-image-tags')).toBeDefined(); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-up'); + done(); + }); + }); + + it('should be closed when the user clicks on an opened repo', (done) => { + vm.$el.querySelector('.js-toggle-repo').click(); + + Vue.nextTick(() => { + vm.$el.querySelector('.js-toggle-repo').click(); + Vue.nextTick(() => { + expect(vm.$el.querySelector('.container-image-tags')).toBe(null); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right'); + done(); + }); + }); + }); + }); + + describe('delete repo', () => { + it('should be possible to delete a repo', () => { + expect(vm.$el.querySelector('.js-remove-repo')).toBeDefined(); + }); + }); + + describe('registry list', () => { + it('should render a table with the registry list', (done) => { + vm.$el.querySelector('.js-toggle-repo').click(); + + Vue.nextTick(() => { + expect( + vm.$el.querySelectorAll('table tbody tr').length, + ).toEqual(mockData.list.length); + done(); + }); + }); + + it('should render registry tag', (done) => { + vm.$el.querySelector('.js-toggle-repo').click(); + + Vue.nextTick(() => { + const textRendered = vm.$el.querySelector('.table tbody tr').textContent.trim().replace(/\s\s+/g, ' '); + expect(textRendered).toContain(mockData.list[0].tag); + expect(textRendered).toContain(mockData.list[0].shortRevision); + expect(textRendered).toContain(mockData.list[0].layers); + expect(textRendered).toContain(mockData.list[0].size); + done(); + }); + }); + + it('should be possible to delete a registry', (done) => { + vm.$el.querySelector('.js-toggle-repo').click(); + + Vue.nextTick(() => { + expect( + vm.$el.querySelector('.table tbody tr .js-delete-registry'), + ).toBeDefined(); + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/registry/stores/actions_spec.js b/spec/javascripts/registry/stores/actions_spec.js new file mode 100644 index 00000000000..d835ea04622 --- /dev/null +++ b/spec/javascripts/registry/stores/actions_spec.js @@ -0,0 +1,85 @@ +import Vue from 'vue'; +import VueResource from 'vue-resource'; +import _ from 'underscore'; +import * as actions from '~/registry/stores/actions'; +import * as types from '~/registry/stores/mutation_types'; +import testAction from '../../helpers/vuex_action_helper'; +import { + defaultState, + reposServerResponse, + registryServerResponse, + parsedReposServerResponse, +} from './mock_data'; + +Vue.use(VueResource); + +describe('Actions Registry Store', () => { + let interceptor; + let mockedState; + + beforeEach(() => { + mockedState = defaultState; + }); + + describe('server requests', () => { + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); + }); + + describe('fetchRepos', () => { + beforeEach(() => { + interceptor = (request, next) => { + next(request.respondWith(JSON.stringify(reposServerResponse), { + status: 200, + })); + }; + + Vue.http.interceptors.push(interceptor); + }); + + it('should set receveived repos', (done) => { + testAction(actions.fetchRepos, null, mockedState, [ + { type: types.TOGGLE_MAIN_LOADING }, + { type: types.SET_REPOS_LIST, payload: reposServerResponse }, + ], done); + }); + }); + + describe('fetchList', () => { + beforeEach(() => { + interceptor = (request, next) => { + next(request.respondWith(JSON.stringify(registryServerResponse), { + status: 200, + })); + }; + + Vue.http.interceptors.push(interceptor); + }); + + it('should set received list', (done) => { + mockedState.repos = parsedReposServerResponse; + + testAction(actions.fetchList, mockedState.repos[1], mockedState, [ + { type: types.TOGGLE_REGISTRY_LIST_LOADING }, + { type: types.SET_REGISTRY_LIST, payload: registryServerResponse }, + ], done); + }); + }); + }); + + describe('setMainEndpoint', () => { + it('should commit set main endpoint', (done) => { + testAction(actions.setMainEndpoint, 'endpoint', mockedState, [ + { type: types.SET_MAIN_ENDPOINT, payload: 'endpoint' }, + ], done); + }); + }); + + describe('toggleLoading', () => { + it('should commit toggle main loading', (done) => { + testAction(actions.toggleLoading, null, mockedState, [ + { type: types.TOGGLE_MAIN_LOADING }, + ], done); + }); + }); +}); diff --git a/spec/javascripts/registry/stores/getters_spec.js b/spec/javascripts/registry/stores/getters_spec.js new file mode 100644 index 00000000000..3d989541881 --- /dev/null +++ b/spec/javascripts/registry/stores/getters_spec.js @@ -0,0 +1,43 @@ +import * as getters from '~/registry/stores/getters'; + +describe('Getters Registry Store', () => { + let state; + + beforeEach(() => { + state = { + isLoading: false, + endpoint: '/root/empty-project/container_registry.json', + repos: [{ + canDelete: true, + destroyPath: 'bar', + id: '134', + isLoading: false, + list: [], + location: 'foo', + name: 'gitlab-org/omnibus-gitlab/foo', + tagsPath: 'foo', + }, { + canDelete: true, + destroyPath: 'bar', + id: '123', + isLoading: false, + list: [], + location: 'foo', + name: 'gitlab-org/omnibus-gitlab', + tagsPath: 'foo', + }], + }; + }); + + describe('isLoading', () => { + it('should return the isLoading property', () => { + expect(getters.isLoading(state)).toEqual(state.isLoading); + }); + }); + + describe('repos', () => { + it('should return the repos', () => { + expect(getters.repos(state)).toEqual(state.repos); + }); + }); +}); diff --git a/spec/javascripts/registry/stores/mock_data.js b/spec/javascripts/registry/stores/mock_data.js new file mode 100644 index 00000000000..80f7c51426a --- /dev/null +++ b/spec/javascripts/registry/stores/mock_data.js @@ -0,0 +1,91 @@ +export const defaultState = { + isLoading: false, + endpoint: '', + repos: [], +}; + +export const reposServerResponse = [ + { + destroy_path: 'path', + id: '123', + location: 'location', + name: 'foo', + tags_path: 'tags_path', + }, + { + destroy_path: 'path_', + id: '456', + location: 'location_', + name: 'bar', + tags_path: 'tags_path_', + }, +]; + +export const registryServerResponse = [ + { + name: 'centos7', + short_revision: 'b118ab5b0', + revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43', + size: 679, + layers: 19, + location: 'location', + created_at: 1505828744434, + destroy_path: 'path_', + }, + { + name: 'centos6', + short_revision: 'b118ab5b0', + revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43', + 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].name, + 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].name, + tagsPath: reposServerResponse[1].tags_path, + }, +]; + +export const parsedRegistryServerResponse = [ + { + tag: registryServerResponse[0].name, + revision: registryServerResponse[0].revision, + shortRevision: registryServerResponse[0].short_revision, + size: registryServerResponse[0].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].size, + layers: registryServerResponse[1].layers, + location: registryServerResponse[1].location, + createdAt: registryServerResponse[1].created_at, + destroyPath: registryServerResponse[1].destroy_path, + canDelete: false, + }, +]; diff --git a/spec/javascripts/registry/stores/mutations_spec.js b/spec/javascripts/registry/stores/mutations_spec.js new file mode 100644 index 00000000000..7fae19f3656 --- /dev/null +++ b/spec/javascripts/registry/stores/mutations_spec.js @@ -0,0 +1,57 @@ +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, mockState.repos[0], registryServerResponse); + + expect(mockState.repos[0].list).toEqual(parsedRegistryServerResponse); + }); + }); + + 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, mockState.repos[0], registryServerResponse); + + mutations[types.TOGGLE_REGISTRY_LIST_LOADING](mockState, mockState.repos[0]); + expect(mockState.repos[0].isLoading).toEqual(true); + }); + }); +}); |