diff options
Diffstat (limited to 'spec/frontend/registry')
11 files changed, 180 insertions, 59 deletions
diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js index ef22979ca7d..3276ef911e3 100644 --- a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js +++ b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js @@ -22,7 +22,7 @@ describe('tags list row', () => { let wrapper; const [tag] = [...tagsListResponse.data]; - const defaultProps = { tag, isDesktop: true, index: 0 }; + const defaultProps = { tag, isMobile: false, index: 0 }; const findCheckbox = () => wrapper.find(GlFormCheckbox); const findName = () => wrapper.find('[data-testid="name"]'); @@ -114,7 +114,7 @@ describe('tags list row', () => { }); it('on mobile has mw-s class', () => { - mountComponent({ ...defaultProps, isDesktop: false }); + mountComponent({ ...defaultProps, isMobile: true }); expect(findName().classes('mw-s')).toBe(true); }); diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js index 401202026bb..ebeaa8ff870 100644 --- a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js +++ b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js @@ -14,7 +14,7 @@ describe('Tags List', () => { const findDeleteButton = () => wrapper.find(GlButton); const findListTitle = () => wrapper.find('[data-testid="list-title"]'); - const mountComponent = (propsData = { tags, isDesktop: true }) => { + const mountComponent = (propsData = { tags, isMobile: false }) => { wrapper = shallowMount(component, { propsData, }); @@ -41,15 +41,15 @@ describe('Tags List', () => { describe('delete button', () => { it.each` - inputTags | isDesktop | isVisible - ${tags} | ${true} | ${true} - ${tags} | ${false} | ${false} - ${readOnlyTags} | ${true} | ${false} - ${readOnlyTags} | ${false} | ${false} + inputTags | isMobile | isVisible + ${tags} | ${false} | ${true} + ${tags} | ${true} | ${false} + ${readOnlyTags} | ${false} | ${false} + ${readOnlyTags} | ${true} | ${false} `( - 'is $isVisible that delete button exists when tags is $inputTags and isDesktop is $isDesktop', - ({ inputTags, isDesktop, isVisible }) => { - mountComponent({ tags: inputTags, isDesktop }); + 'is $isVisible that delete button exists when tags is $inputTags and isMobile is $isMobile', + ({ inputTags, isMobile, isVisible }) => { + mountComponent({ tags: inputTags, isMobile }); expect(findDeleteButton().exists()).toBe(isVisible); }, @@ -110,12 +110,6 @@ describe('Tags List', () => { expect(rows.at(0).attributes()).toMatchObject({ first: 'true', - isdesktop: 'true', - }); - - // The list has only two tags and for some reasons .at(-1) does not work - expect(rows.at(1).attributes()).toMatchObject({ - isdesktop: 'true', }); }); diff --git a/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js b/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js index b4471ab8122..551d1eee68d 100644 --- a/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js +++ b/spec/frontend/registry/explorer/components/list_page/cli_commands_spec.js @@ -1,6 +1,6 @@ import Vuex from 'vuex'; import { mount, createLocalVue } from '@vue/test-utils'; -import { GlDeprecatedDropdown } from '@gitlab/ui'; +import { GlDropdown } from '@gitlab/ui'; import Tracking from '~/tracking'; import * as getters from '~/registry/explorer/stores/getters'; import QuickstartDropdown from '~/registry/explorer/components/list_page/cli_commands.vue'; @@ -23,7 +23,7 @@ describe('cli_commands', () => { let wrapper; let store; - const findDropdownButton = () => wrapper.find(GlDeprecatedDropdown); + const findDropdownButton = () => wrapper.find(GlDropdown); const findCodeInstruction = () => wrapper.findAll(CodeInstruction); const mountComponent = () => { diff --git a/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js index ce446e6d93e..9f7a2758ae1 100644 --- a/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js +++ b/spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js @@ -19,7 +19,7 @@ describe('Image List Row', () => { let wrapper; const item = imagesListResponse.data[0]; - const findDetailsLink = () => wrapper.find('[data-testid="detailsLink"]'); + const findDetailsLink = () => wrapper.find('[data-testid="details-link"]'); const findTagsCount = () => wrapper.find('[data-testid="tagsCount"]'); const findDeleteBtn = () => wrapper.find(DeleteButton); const findClipboardButton = () => wrapper.find(ClipboardButton); @@ -67,7 +67,12 @@ describe('Image List Row', () => { mountComponent(); const link = findDetailsLink(); expect(link.html()).toContain(item.path); - expect(link.props('to').name).toBe('details'); + expect(link.props('to')).toMatchObject({ + name: 'details', + params: { + id: item.id, + }, + }); }); it('contains a clipboard button', () => { diff --git a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js index b906e44a4f7..d730bfcde24 100644 --- a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js +++ b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js @@ -32,6 +32,10 @@ describe('Registry Breadcrumb', () => { { name: 'baz', meta: { nameGenerator } }, ]; + const state = { + imageDetails: { foo: 'bar' }, + }; + const findDivider = () => wrapper.find('.js-divider'); const findRootRoute = () => wrapper.find({ ref: 'rootRouteLink' }); const findChildRoute = () => wrapper.find({ ref: 'childRouteLink' }); @@ -52,6 +56,9 @@ describe('Registry Breadcrumb', () => { routes, }, }, + $store: { + state, + }, }, }); }; @@ -80,7 +87,7 @@ describe('Registry Breadcrumb', () => { }); it('the link text is calculated by nameGenerator', () => { - expect(nameGenerator).toHaveBeenCalledWith(routes[0]); + expect(nameGenerator).toHaveBeenCalledWith(state); expect(nameGenerator).toHaveBeenCalledTimes(1); }); }); @@ -104,7 +111,7 @@ describe('Registry Breadcrumb', () => { }); it('the link text is calculated by nameGenerator', () => { - expect(nameGenerator).toHaveBeenCalledWith(routes[1]); + expect(nameGenerator).toHaveBeenCalledWith(state); expect(nameGenerator).toHaveBeenCalledTimes(2); }); }); diff --git a/spec/frontend/registry/explorer/mock_data.js b/spec/frontend/registry/explorer/mock_data.js index a7ffed4c9fd..da5f1840b5c 100644 --- a/spec/frontend/registry/explorer/mock_data.js +++ b/spec/frontend/registry/explorer/mock_data.js @@ -97,3 +97,14 @@ export const imagePagination = { totalPages: 2, nextPage: 2, }; + +export const imageDetailsMock = { + id: 1, + name: 'rails-32309', + path: 'gitlab-org/gitlab-test/rails-32309', + project_id: 1, + location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-32309', + created_at: '2020-06-29T10:23:47.838Z', + cleanup_policy_started_at: null, + delete_api_path: 'http://0.0.0.0:3000/api/v4/projects/1/registry/repositories/1', +}; diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 86b52c4f06a..c09b7e0c067 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -14,9 +14,10 @@ import { SET_TAGS_LIST_SUCCESS, SET_TAGS_PAGINATION, SET_INITIAL_STATE, + SET_IMAGE_DETAILS, } from '~/registry/explorer/stores/mutation_types'; -import { tagsListResponse } from '../mock_data'; +import { tagsListResponse, imageDetailsMock } from '../mock_data'; import { DeleteModal } from '../stubs'; describe('Details Page', () => { @@ -33,8 +34,7 @@ describe('Details Page', () => { const findEmptyTagsState = () => wrapper.find(EmptyTagsState); const findPartialCleanupAlert = () => wrapper.find(PartialCleanupAlert); - const routeIdGenerator = override => - window.btoa(JSON.stringify({ name: 'foo', tags_path: 'bar', ...override })); + const routeId = 1; const tagsArrayToSelectedTags = tags => tags.reduce((acc, c) => { @@ -42,7 +42,7 @@ describe('Details Page', () => { return acc; }, {}); - const mountComponent = ({ options, routeParams } = {}) => { + const mountComponent = ({ options } = {}) => { wrapper = shallowMount(component, { store, stubs: { @@ -51,7 +51,7 @@ describe('Details Page', () => { mocks: { $route: { params: { - id: routeIdGenerator(routeParams), + id: routeId, }, }, }, @@ -65,6 +65,7 @@ describe('Details Page', () => { dispatchSpy.mockResolvedValue(); store.commit(SET_TAGS_LIST_SUCCESS, tagsListResponse.data); store.commit(SET_TAGS_PAGINATION, tagsListResponse.headers); + store.commit(SET_IMAGE_DETAILS, imageDetailsMock); jest.spyOn(Tracking, 'event'); }); @@ -73,6 +74,13 @@ describe('Details Page', () => { wrapper = null; }); + describe('lifecycle events', () => { + it('calls the appropriate action on mount', () => { + mountComponent(); + expect(dispatchSpy).toHaveBeenCalledWith('requestImageDetailsAndTagsList', routeId); + }); + }); + describe('when isLoading is true', () => { beforeEach(() => { store.commit(SET_MAIN_LOADING, true); @@ -124,7 +132,7 @@ describe('Details Page', () => { it('has the correct props bound', () => { expect(findTagsList().props()).toMatchObject({ - isDesktop: true, + isMobile: false, tags: store.state.tags, }); }); @@ -194,8 +202,7 @@ describe('Details Page', () => { dispatchSpy.mockResolvedValue(); findPagination().vm.$emit(GlPagination.model.event, 2); expect(store.dispatch).toHaveBeenCalledWith('requestTagsList', { - params: wrapper.vm.$route.params.id, - pagination: { page: 2 }, + page: 2, }); }); }); @@ -227,7 +234,6 @@ describe('Details Page', () => { findDeleteModal().vm.$emit('confirmDelete'); expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTag', { tag: store.state.tags[0], - params: routeIdGenerator(), }); }); }); @@ -242,7 +248,6 @@ describe('Details Page', () => { findDeleteModal().vm.$emit('confirmDelete'); expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTags', { ids: store.state.tags.map(t => t.name), - params: routeIdGenerator(), }); }); }); @@ -257,7 +262,7 @@ describe('Details Page', () => { it('has the correct props', () => { mountComponent(); - expect(findDetailsHeader().props()).toEqual({ imageName: 'foo' }); + expect(findDetailsHeader().props()).toEqual({ imageName: imageDetailsMock.name }); }); }); @@ -293,10 +298,14 @@ describe('Details Page', () => { }; describe('when expiration_policy_started is not null', () => { - const routeParams = { cleanup_policy_started_at: Date.now().toString() }; - + beforeEach(() => { + store.commit(SET_IMAGE_DETAILS, { + ...imageDetailsMock, + cleanup_policy_started_at: Date.now().toString(), + }); + }); it('exists', () => { - mountComponent({ routeParams }); + mountComponent(); expect(findPartialCleanupAlert().exists()).toBe(true); }); @@ -304,13 +313,13 @@ describe('Details Page', () => { it('has the correct props', () => { store.commit(SET_INITIAL_STATE, { ...config }); - mountComponent({ routeParams }); + mountComponent(); expect(findPartialCleanupAlert().props()).toEqual({ ...config }); }); it('dismiss hides the component', async () => { - mountComponent({ routeParams }); + mountComponent(); expect(findPartialCleanupAlert().exists()).toBe(true); findPartialCleanupAlert().vm.$emit('dismiss'); diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js index fb93ab06ca8..dcd4d8015a4 100644 --- a/spec/frontend/registry/explorer/stores/actions_spec.js +++ b/spec/frontend/registry/explorer/stores/actions_spec.js @@ -1,18 +1,29 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; import { TEST_HOST } from 'helpers/test_constants'; +import createFlash from '~/flash'; +import Api from '~/api'; import axios from '~/lib/utils/axios_utils'; import * as actions from '~/registry/explorer/stores/actions'; import * as types from '~/registry/explorer/stores/mutation_types'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; import { reposServerResponse, registryServerResponse } from '../mock_data'; +import * as utils from '~/registry/explorer/utils'; +import { + FETCH_IMAGES_LIST_ERROR_MESSAGE, + FETCH_TAGS_LIST_ERROR_MESSAGE, + FETCH_IMAGE_DETAILS_ERROR_MESSAGE, +} from '~/registry/explorer/constants/index'; jest.mock('~/flash.js'); +jest.mock('~/registry/explorer/utils'); describe('Actions RegistryExplorer Store', () => { let mock; const endpoint = `${TEST_HOST}/endpoint.json`; + const url = `${endpoint}/1}`; + jest.spyOn(utils, 'pathGenerator').mockReturnValue(url); + beforeEach(() => { mock = new MockAdapter(axios); }); @@ -123,7 +134,7 @@ describe('Actions RegistryExplorer Store', () => { ], [], () => { - expect(createFlash).toHaveBeenCalled(); + expect(createFlash).toHaveBeenCalledWith({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE }); done(); }, ); @@ -131,15 +142,12 @@ describe('Actions RegistryExplorer Store', () => { }); describe('fetch tags list', () => { - const url = `${endpoint}/1}`; - const params = window.btoa(JSON.stringify({ tags_path: `${endpoint}/1}` })); - it('sets the tagsList', done => { mock.onGet(url).replyOnce(200, registryServerResponse, {}); testAction( actions.requestTagsList, - { params }, + {}, {}, [ { type: types.SET_MAIN_LOADING, payload: true }, @@ -158,7 +166,7 @@ describe('Actions RegistryExplorer Store', () => { it('should create flash on error', done => { testAction( actions.requestTagsList, - { params }, + {}, {}, [ { type: types.SET_MAIN_LOADING, payload: true }, @@ -166,7 +174,7 @@ describe('Actions RegistryExplorer Store', () => { ], [], () => { - expect(createFlash).toHaveBeenCalled(); + expect(createFlash).toHaveBeenCalledWith({ message: FETCH_TAGS_LIST_ERROR_MESSAGE }); done(); }, ); @@ -176,8 +184,6 @@ describe('Actions RegistryExplorer Store', () => { describe('request delete single tag', () => { it('successfully performs the delete request', done => { const deletePath = 'delete/path'; - const params = window.btoa(JSON.stringify({ tags_path: `${endpoint}/1}`, id: 1 })); - mock.onDelete(deletePath).replyOnce(200); testAction( @@ -186,7 +192,6 @@ describe('Actions RegistryExplorer Store', () => { tag: { destroy_path: deletePath, }, - params, }, { tagsPagination: {}, @@ -202,7 +207,7 @@ describe('Actions RegistryExplorer Store', () => { }, { type: 'requestTagsList', - payload: { pagination: {}, params }, + payload: {}, }, ], done, @@ -227,18 +232,55 @@ describe('Actions RegistryExplorer Store', () => { }); }); - describe('request delete multiple tags', () => { - const url = `project-path/registry/repository/foo/tags`; - const params = window.btoa(JSON.stringify({ tags_path: `${url}?format=json` })); + describe('requestImageDetailsAndTagsList', () => { + it('sets the imageDetails and dispatch requestTagsList', done => { + const resolvedValue = { foo: 'bar' }; + jest.spyOn(Api, 'containerRegistryDetails').mockResolvedValue({ data: resolvedValue }); + + testAction( + actions.requestImageDetailsAndTagsList, + 1, + {}, + [ + { type: types.SET_MAIN_LOADING, payload: true }, + { type: types.SET_IMAGE_DETAILS, payload: resolvedValue }, + ], + [ + { + type: 'requestTagsList', + }, + ], + done, + ); + }); + + it('should create flash on error', done => { + jest.spyOn(Api, 'containerRegistryDetails').mockRejectedValue(); + testAction( + actions.requestImageDetailsAndTagsList, + 1, + {}, + [ + { type: types.SET_MAIN_LOADING, payload: true }, + { type: types.SET_MAIN_LOADING, payload: false }, + ], + [], + () => { + expect(createFlash).toHaveBeenCalledWith({ message: FETCH_IMAGE_DETAILS_ERROR_MESSAGE }); + done(); + }, + ); + }); + }); + describe('request delete multiple tags', () => { it('successfully performs the delete request', done => { - mock.onDelete(`${url}/bulk_destroy`).replyOnce(200); + mock.onDelete(url).replyOnce(200); testAction( actions.requestDeleteTags, { ids: [1, 2], - params, }, { tagsPagination: {}, @@ -254,7 +296,7 @@ describe('Actions RegistryExplorer Store', () => { }, { type: 'requestTagsList', - payload: { pagination: {}, params }, + payload: {}, }, ], done, @@ -268,7 +310,6 @@ describe('Actions RegistryExplorer Store', () => { actions.requestDeleteTags, { ids: [1, 2], - params, }, { tagsPagination: {}, diff --git a/spec/frontend/registry/explorer/stores/mutations_spec.js b/spec/frontend/registry/explorer/stores/mutations_spec.js index 4ca0211cdc3..1908d3f0350 100644 --- a/spec/frontend/registry/explorer/stores/mutations_spec.js +++ b/spec/frontend/registry/explorer/stores/mutations_spec.js @@ -121,4 +121,13 @@ describe('Mutations Registry Explorer Store', () => { expect(mockState).toEqual(expectedState); }); }); + + describe('SET_IMAGE_DETAILS', () => { + it('should set imageDetails', () => { + const expectedState = { ...mockState, imageDetails: { foo: 'bar' } }; + mutations[types.SET_IMAGE_DETAILS](mockState, { foo: 'bar' }); + + expect(mockState).toEqual(expectedState); + }); + }); }); diff --git a/spec/frontend/registry/explorer/utils_spec.js b/spec/frontend/registry/explorer/utils_spec.js new file mode 100644 index 00000000000..0cd4a1cec29 --- /dev/null +++ b/spec/frontend/registry/explorer/utils_spec.js @@ -0,0 +1,45 @@ +import { pathGenerator } from '~/registry/explorer/utils'; + +describe('Utils', () => { + describe('pathGenerator', () => { + const imageDetails = { + path: 'foo/bar/baz', + name: 'baz', + id: 1, + }; + + it('returns the fetch url when no ending is passed', () => { + expect(pathGenerator(imageDetails)).toBe('/foo/bar/registry/repository/1/tags?format=json'); + }); + + it('returns the url with an ending when is passed', () => { + expect(pathGenerator(imageDetails, '/foo')).toBe('/foo/bar/registry/repository/1/tags/foo'); + }); + + it.each` + path | name | result + ${'foo/foo'} | ${''} | ${'/foo/foo/registry/repository/1/tags?format=json'} + ${'foo/foo/foo'} | ${'foo'} | ${'/foo/foo/registry/repository/1/tags?format=json'} + ${'baz/foo/foo/foo'} | ${'foo'} | ${'/baz/foo/foo/registry/repository/1/tags?format=json'} + ${'baz/foo/foo/foo'} | ${'foo'} | ${'/baz/foo/foo/registry/repository/1/tags?format=json'} + ${'foo/foo/baz/foo/foo'} | ${'foo/foo'} | ${'/foo/foo/baz/registry/repository/1/tags?format=json'} + ${'foo/foo/baz/foo/bar'} | ${'foo/bar'} | ${'/foo/foo/baz/registry/repository/1/tags?format=json'} + ${'baz/foo/foo'} | ${'foo'} | ${'/baz/foo/registry/repository/1/tags?format=json'} + ${'baz/foo/bar'} | ${'foo'} | ${'/baz/foo/bar/registry/repository/1/tags?format=json'} + `('returns the correct path when path is $path and name is $name', ({ name, path, result }) => { + expect(pathGenerator({ id: 1, name, path })).toBe(result); + }); + + it('returns the url unchanged when imageDetails have no name', () => { + const imageDetailsWithoutName = { + path: 'foo/bar/baz', + name: '', + id: 1, + }; + + expect(pathGenerator(imageDetailsWithoutName)).toBe( + '/foo/bar/baz/registry/repository/1/tags?format=json', + ); + }); + }); +}); diff --git a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap index 69953fb5e03..2ceb2655d40 100644 --- a/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap +++ b/spec/frontend/registry/shared/components/__snapshots__/expiration_policy_fields_spec.js.snap @@ -123,7 +123,7 @@ exports[`Expiration Policy Form renders 1`] = ` disabled="true" id="expiration-policy-name-matching" noresize="true" - placeholder=".*" + placeholder="" trim="" value="" /> |