diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 18:10:12 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 18:10:12 +0300 |
commit | 8f143a46faf2e7b594301512757edf372c294a0c (patch) | |
tree | 8bd5957ffa44d028905ab51a7252cce6783d2e25 /spec/frontend/registry | |
parent | 3e06afc4cd1b75b3e957e8debf5e4f1963ba18e0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/registry')
5 files changed, 381 insertions, 181 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 3276ef911e3..9a5a8b8b7a7 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 @@ -20,7 +20,7 @@ import { ListItem } from '../../stubs'; describe('tags list row', () => { let wrapper; - const [tag] = [...tagsListResponse.data]; + const [tag] = [...tagsListResponse]; const defaultProps = { tag, isMobile: false, index: 0 }; @@ -65,7 +65,7 @@ describe('tags list row', () => { }); it("does not exist when the row can't be deleted", () => { - const customTag = { ...tag, destroy_path: '' }; + const customTag = { ...tag, canDelete: false }; mountComponent({ ...defaultProps, tag: customTag }); @@ -137,8 +137,8 @@ describe('tags list row', () => { mountComponent(); expect(findClipboardButton().attributes()).toMatchObject({ - text: 'location', - title: 'location', + text: tag.location, + title: tag.location, }); }); }); @@ -171,26 +171,26 @@ describe('tags list row', () => { expect(findSize().exists()).toBe(true); }); - it('contains the total_size and layers', () => { - mountComponent({ ...defaultProps, tag: { ...tag, total_size: 1024 } }); + it('contains the totalSize and layers', () => { + mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 1024 } }); expect(findSize().text()).toMatchInterpolatedText('1.00 KiB · 10 layers'); }); - it('when total_size is missing', () => { - mountComponent(); + it('when totalSize is missing', () => { + mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 0 } }); expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 10 layers`); }); it('when layers are missing', () => { - mountComponent({ ...defaultProps, tag: { ...tag, total_size: 1024, layers: null } }); + mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 1024, layers: null } }); expect(findSize().text()).toMatchInterpolatedText('1.00 KiB'); }); it('when there is 1 layer', () => { - mountComponent({ ...defaultProps, tag: { ...tag, layers: 1 } }); + mountComponent({ ...defaultProps, tag: { ...tag, totalSize: 0, layers: 1 } }); expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 1 layer`); }); @@ -218,7 +218,7 @@ describe('tags list row', () => { it('pass the correct props to time ago tooltip', () => { mountComponent(); - expect(findTimeAgoTooltip().attributes()).toMatchObject({ time: tag.created_at }); + expect(findTimeAgoTooltip().attributes()).toMatchObject({ time: tag.createdAt }); }); }); @@ -232,7 +232,7 @@ describe('tags list row', () => { it('has the correct text', () => { mountComponent(); - expect(findShortRevision().text()).toMatchInterpolatedText('Digest: 1ab51d5'); + expect(findShortRevision().text()).toMatchInterpolatedText('Digest: 9d72ae1'); }); it(`displays ${NOT_AVAILABLE_TEXT} when digest is missing`, () => { @@ -260,18 +260,15 @@ describe('tags list row', () => { }); it.each` - destroy_path | digest - ${'foo'} | ${null} - ${null} | ${'foo'} - ${null} | ${null} - `( - 'is disabled when destroy_path is $destroy_path and digest is $digest', - ({ destroy_path, digest }) => { - mountComponent({ ...defaultProps, tag: { ...tag, destroy_path, digest } }); - - expect(findDeleteButton().attributes('disabled')).toBe('true'); - }, - ); + canDelete | digest + ${true} | ${null} + ${false} | ${'foo'} + ${false} | ${null} + `('is disabled when canDelete is $canDelete and digest is $digest', ({ canDelete, digest }) => { + mountComponent({ ...defaultProps, tag: { ...tag, canDelete, digest } }); + + expect(findDeleteButton().attributes('disabled')).toBe('true'); + }); it('delete event emits delete', () => { mountComponent(); @@ -295,10 +292,10 @@ describe('tags list row', () => { }); describe.each` - name | finderFunction | text | icon | clipboard - ${'published date detail'} | ${findPublishedDateDetail} | ${'Published to the bar image repository at 10:23 GMT+0000 on 2020-06-29'} | ${'clock'} | ${false} - ${'manifest detail'} | ${findManifestDetail} | ${'Manifest digest: sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7786dfd5c'} | ${'log'} | ${true} - ${'configuration detail'} | ${findConfigurationDetail} | ${'Configuration digest: sha256:b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43'} | ${'cloud-gear'} | ${true} + name | finderFunction | text | icon | clipboard + ${'published date detail'} | ${findPublishedDateDetail} | ${'Published to the gitlab-org/gitlab-test/rails-12009 image repository at 01:29 GMT+0000 on 2020-11-03'} | ${'clock'} | ${false} + ${'manifest detail'} | ${findManifestDetail} | ${'Manifest digest: sha256:9d72ae1db47404e44e1760eb1ca4cb427b84be8c511f05dfe2089e1b9f741dd7'} | ${'log'} | ${true} + ${'configuration detail'} | ${findConfigurationDetail} | ${'Configuration digest: sha256:5183b5d133fa864dca2de602f874b0d1bffe0f204ad894e3660432a487935139'} | ${'cloud-gear'} | ${true} `('$name details row', ({ finderFunction, text, icon, clipboard }) => { it(`has ${text} as text`, () => { expect(finderFunction().text()).toMatchInterpolatedText(text); 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 ebeaa8ff870..e5779b2c103 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 @@ -7,8 +7,8 @@ import { tagsListResponse } from '../../mock_data'; describe('Tags List', () => { let wrapper; - const tags = [...tagsListResponse.data]; - const readOnlyTags = tags.map(t => ({ ...t, destroy_path: undefined })); + const tags = [...tagsListResponse]; + const readOnlyTags = tags.map(t => ({ ...t, canDelete: false })); const findTagsListRow = () => wrapper.findAll(TagsListRow); const findDeleteButton = () => wrapper.find(GlButton); @@ -92,7 +92,7 @@ describe('Tags List', () => { .vm.$emit('select'); findDeleteButton().vm.$emit('click'); - expect(wrapper.emitted('delete')).toEqual([[{ centos6: true }]]); + expect(wrapper.emitted('delete')).toEqual([[{ 'alpha-11821': true }]]); }); }); @@ -132,7 +132,7 @@ describe('Tags List', () => { findTagsListRow() .at(0) .vm.$emit('delete'); - expect(wrapper.emitted('delete')).toEqual([[{ centos6: true }]]); + expect(wrapper.emitted('delete')).toEqual([[{ 'alpha-11821': true }]]); }); }); }); diff --git a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js index d730bfcde24..fb0b98ba004 100644 --- a/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js +++ b/spec/frontend/registry/explorer/components/registry_breadcrumb_spec.js @@ -32,10 +32,6 @@ 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' }); @@ -56,9 +52,6 @@ describe('Registry Breadcrumb', () => { routes, }, }, - $store: { - state, - }, }, }); }; @@ -87,7 +80,6 @@ describe('Registry Breadcrumb', () => { }); it('the link text is calculated by nameGenerator', () => { - expect(nameGenerator).toHaveBeenCalledWith(state); expect(nameGenerator).toHaveBeenCalledTimes(1); }); }); @@ -111,7 +103,6 @@ describe('Registry Breadcrumb', () => { }); it('the link text is calculated by nameGenerator', () => { - 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 13b1c4a485d..06a25470c45 100644 --- a/spec/frontend/registry/explorer/mock_data.js +++ b/spec/frontend/registry/explorer/mock_data.js @@ -72,34 +72,34 @@ export const imagesListResponse = [ }, ]; -export const tagsListResponse = { - data: [ - { - name: 'centos6', - revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43', - short_revision: 'b118ab5b0', - size: 19, - layers: 10, - location: 'location', - path: 'bar:centos6', - created_at: '2020-06-29T10:23:51.766+00:00', - destroy_path: 'path', - digest: 'sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7786dfd5c', - }, - { - name: 'test-tag', - revision: 'b969de599faea2b3d9b6605a8b0897261c571acaa36db1bdc7349b5775b4e0b4', - short_revision: 'b969de599', - size: 19, - layers: 10, - path: 'foo:test-tag', - location: 'location-2', - created_at: '2020-06-29T10:23:51.766+00:00', - digest: 'sha256:1ab51d519f574b636ae7788051c60239334ae8622a9fd82a0cf7bae7736dfd5c', - }, - ], - headers, -}; +export const tagsListResponse = [ + { + canDelete: true, + createdAt: '2020-11-03T13:29:49+00:00', + digest: 'sha256:9d72ae1db47404e44e1760eb1ca4cb427b84be8c511f05dfe2089e1b9f741dd7', + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:alpha-11821', + name: 'alpha-11821', + path: 'gitlab-org/gitlab-test/rails-12009:alpha-11821', + revision: '5183b5d133fa864dca2de602f874b0d1bffe0f204ad894e3660432a487935139', + shortRevision: '5183b5d13', + totalSize: 104, + layers: 10, + __typename: 'ContainerRepositoryTag', + }, + { + canDelete: true, + createdAt: '2020-11-03T13:29:48+00:00', + digest: 'sha256:64f61282a71659f72066f9decd30b9038a465859b277a5e20da8681eb83e72f7', + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:alpha-20825', + name: 'alpha-20825', + path: 'gitlab-org/gitlab-test/rails-12009:alpha-20825', + revision: 'e4212f1b73c6f9def2c37fa7df6c8d35c345fb1402860ff9a56404821aacf16f', + shortRevision: 'e4212f1b7', + totalSize: 105, + layers: 10, + __typename: 'ContainerRepositoryTag', + }, +]; export const pageInfo = { hasNextPage: true, @@ -110,14 +110,14 @@ export const pageInfo = { }; 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', + canDelete: true, + createdAt: '2020-11-03T13:29:21Z', + expirationPolicyStartedAt: null, + id: 'gid://gitlab/ContainerRepository/26', + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009', + name: 'rails-12009', + path: 'gitlab-org/gitlab-test/rails-12009', + status: null, }; export const graphQLImageListMock = { @@ -192,3 +192,96 @@ export const graphQLImageDeleteMockError = { }, }, }; + +export const containerRepositoryMock = { + id: 'gid://gitlab/ContainerRepository/26', + name: 'rails-12009', + path: 'gitlab-org/gitlab-test/rails-12009', + status: null, + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009', + canDelete: true, + createdAt: '2020-11-03T13:29:21Z', + tagsCount: 13, + expirationPolicyStartedAt: null, +}; + +export const tagsPageInfo = { + __typename: 'PageInfo', + hasNextPage: true, + hasPreviousPage: true, + startCursor: 'MQ', + endCursor: 'MTA', +}; + +export const tagsMock = [ + { + digest: 'sha256:2cf3d2fdac1b04a14301d47d51cb88dcd26714c74f91440eeee99ce399089062', + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:beta-24753', + path: 'gitlab-org/gitlab-test/rails-12009:beta-24753', + name: 'beta-24753', + revision: 'c2613843ab33aabf847965442b13a8b55a56ae28837ce182627c0716eb08c02b', + shortRevision: 'c2613843a', + createdAt: '2020-11-03T13:29:38+00:00', + totalSize: 105, + canDelete: true, + __typename: 'ContainerRepositoryTag', + }, + { + digest: 'sha256:7f94f97dff89ffd122cafe50cd32329adf682356a7a96f69cbfe313ee589791c', + location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009:beta-31075', + path: 'gitlab-org/gitlab-test/rails-12009:beta-31075', + name: 'beta-31075', + revision: 'df44e7228f0f255c73e35b6f0699624a615f42746e3e8e2e4b3804a6d6fc3292', + shortRevision: 'df44e7228', + createdAt: '2020-11-03T13:29:32+00:00', + totalSize: 104, + canDelete: true, + __typename: 'ContainerRepositoryTag', + }, +]; + +export const graphQLImageDetailsMock = override => ({ + data: { + containerRepository: { + ...containerRepositoryMock, + + tags: { + nodes: tagsMock, + pageInfo: { ...tagsPageInfo }, + __typename: 'ContainerRepositoryTagConnection', + }, + __typename: 'ContainerRepositoryDetails', + ...override, + }, + }, +}); + +export const graphQLImageDetailsEmptyTagsMock = { + data: { + containerRepository: { + ...containerRepositoryMock, + tags: { + nodes: [], + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', + }, + __typename: 'ContainerRepositoryTagConnection', + }, + __typename: 'ContainerRepositoryDetails', + }, + }, +}; + +export const graphQLDeleteImageRepositoryTagsMock = { + data: { + destroyContainerRepositoryTags: { + deletedTagNames: [], + errors: [], + __typename: 'DestroyContainerRepositoryTagsPayload', + }, + }, +}; diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index c09b7e0c067..b2b0d4de9b9 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -1,5 +1,8 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlPagination } from '@gitlab/ui'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { GlKeysetPagination } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'jest/helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import Tracking from '~/tracking'; import component from '~/registry/explorer/pages/details.vue'; import DeleteAlert from '~/registry/explorer/components/details_page/delete_alert.vue'; @@ -9,24 +12,29 @@ import TagsLoader from '~/registry/explorer/components/details_page/tags_loader. import TagsList from '~/registry/explorer/components/details_page/tags_list.vue'; import EmptyTagsState from '~/registry/explorer/components/details_page/empty_tags_state.vue'; import { createStore } from '~/registry/explorer/stores/'; + +import getContainerRepositoryDetailsQuery from '~/registry/explorer/graphql/queries/get_container_repository_details.graphql'; +import deleteContainerRepositoryTagsMutation from '~/registry/explorer/graphql/mutations/delete_container_repository_tags.graphql'; + import { - SET_MAIN_LOADING, - SET_TAGS_LIST_SUCCESS, - SET_TAGS_PAGINATION, - SET_INITIAL_STATE, - SET_IMAGE_DETAILS, -} from '~/registry/explorer/stores/mutation_types'; - -import { tagsListResponse, imageDetailsMock } from '../mock_data'; + graphQLImageDetailsMock, + graphQLImageDetailsEmptyTagsMock, + graphQLDeleteImageRepositoryTagsMock, + containerRepositoryMock, + tagsMock, + tagsPageInfo, +} from '../mock_data'; import { DeleteModal } from '../stubs'; +const localVue = createLocalVue(); + describe('Details Page', () => { let wrapper; - let dispatchSpy; let store; + let apolloProvider; const findDeleteModal = () => wrapper.find(DeleteModal); - const findPagination = () => wrapper.find(GlPagination); + const findPagination = () => wrapper.find(GlKeysetPagination); const findTagsLoader = () => wrapper.find(TagsLoader); const findTagsList = () => wrapper.find(TagsList); const findDeleteAlert = () => wrapper.find(DeleteAlert); @@ -36,15 +44,46 @@ describe('Details Page', () => { const routeId = 1; + const breadCrumbState = { + updateName: jest.fn(), + }; + + const cleanTags = tagsMock.map(t => { + const result = { ...t }; + // eslint-disable-next-line no-underscore-dangle + delete result.__typename; + return result; + }); + + const waitForApolloRequestRender = async () => { + await waitForPromises(); + await wrapper.vm.$nextTick(); + }; + const tagsArrayToSelectedTags = tags => tags.reduce((acc, c) => { acc[c.name] = true; return acc; }, {}); - const mountComponent = ({ options } = {}) => { + const mountComponent = ({ + resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()), + mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock), + options, + } = {}) => { + localVue.use(VueApollo); + + const requestHandlers = [ + [getContainerRepositoryDetailsQuery, resolver], + [deleteContainerRepositoryTagsMutation, mutationResolver], + ]; + + apolloProvider = createMockApollo(requestHandlers); + wrapper = shallowMount(component, { store, + localVue, + apolloProvider, stubs: { DeleteModal, }, @@ -55,17 +94,17 @@ describe('Details Page', () => { }, }, }, + provide() { + return { + breadCrumbState, + }; + }, ...options, }); }; beforeEach(() => { store = createStore(); - dispatchSpy = jest.spyOn(store, 'dispatch'); - 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'); }); @@ -74,85 +113,90 @@ 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); + it('shows the loader', () => { mountComponent(); - }); - - afterEach(() => store.commit(SET_MAIN_LOADING, false)); - it('shows the loader', () => { expect(findTagsLoader().exists()).toBe(true); }); it('does not show the list', () => { + mountComponent(); + expect(findTagsList().exists()).toBe(false); }); it('does not show pagination', () => { + mountComponent(); + expect(findPagination().exists()).toBe(false); }); }); describe('when the list of tags is empty', () => { - beforeEach(() => { - store.commit(SET_TAGS_LIST_SUCCESS, []); - mountComponent(); - }); + const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock); + + it('has the empty state', async () => { + mountComponent({ resolver }); + + await waitForApolloRequestRender(); - it('has the empty state', () => { expect(findEmptyTagsState().exists()).toBe(true); }); - it('does not show the loader', () => { + it('does not show the loader', async () => { + mountComponent({ resolver }); + + await waitForApolloRequestRender(); + expect(findTagsLoader().exists()).toBe(false); }); - it('does not show the list', () => { + it('does not show the list', async () => { + mountComponent({ resolver }); + + await waitForApolloRequestRender(); + expect(findTagsList().exists()).toBe(false); }); }); describe('list', () => { - beforeEach(() => { + it('exists', async () => { mountComponent(); - }); - it('exists', () => { + await waitForApolloRequestRender(); + expect(findTagsList().exists()).toBe(true); }); - it('has the correct props bound', () => { + it('has the correct props bound', async () => { + mountComponent(); + + await waitForApolloRequestRender(); + expect(findTagsList().props()).toMatchObject({ isMobile: false, - tags: store.state.tags, + tags: cleanTags, }); }); describe('deleteEvent', () => { describe('single item', () => { let tagToBeDeleted; - beforeEach(() => { - [tagToBeDeleted] = store.state.tags; + beforeEach(async () => { + mountComponent(); + + await waitForApolloRequestRender(); + + [tagToBeDeleted] = cleanTags; findTagsList().vm.$emit('delete', { [tagToBeDeleted.name]: true }); }); - it('open the modal', () => { + it('open the modal', async () => { expect(DeleteModal.methods.show).toHaveBeenCalled(); }); - it('maps the selection to itemToBeDeleted', () => { - expect(wrapper.vm.itemsToBeDeleted).toEqual([tagToBeDeleted]); - }); - it('tracks a single delete event', () => { expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { label: 'registry_tag_delete', @@ -161,18 +205,18 @@ describe('Details Page', () => { }); describe('multiple items', () => { - beforeEach(() => { - findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags)); + beforeEach(async () => { + mountComponent(); + + await waitForApolloRequestRender(); + + findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(cleanTags)); }); it('open the modal', () => { expect(DeleteModal.methods.show).toHaveBeenCalled(); }); - it('maps the selection to itemToBeDeleted', () => { - expect(wrapper.vm.itemsToBeDeleted).toEqual(store.state.tags); - }); - it('tracks a single delete event', () => { expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { label: 'bulk_registry_tag_delete', @@ -183,40 +227,77 @@ describe('Details Page', () => { }); describe('pagination', () => { - beforeEach(() => { + it('exists', async () => { mountComponent(); - }); - it('exists', () => { + await waitForApolloRequestRender(); + expect(findPagination().exists()).toBe(true); }); - it('is wired to the correct pagination props', () => { - const pagination = findPagination(); - expect(pagination.props('perPage')).toBe(store.state.tagsPagination.perPage); - expect(pagination.props('totalItems')).toBe(store.state.tagsPagination.total); - expect(pagination.props('value')).toBe(store.state.tagsPagination.page); + it('is hidden when there are no more pages', async () => { + mountComponent({ resolver: jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock) }); + + await waitForApolloRequestRender(); + + expect(findPagination().exists()).toBe(false); }); - it('fetch the data from the API when the v-model changes', () => { - dispatchSpy.mockResolvedValue(); - findPagination().vm.$emit(GlPagination.model.event, 2); - expect(store.dispatch).toHaveBeenCalledWith('requestTagsList', { - page: 2, + it('is wired to the correct pagination props', async () => { + mountComponent(); + + await waitForApolloRequestRender(); + + expect(findPagination().props()).toMatchObject({ + hasNextPage: tagsPageInfo.hasNextPage, + hasPreviousPage: tagsPageInfo.hasPreviousPage, }); }); + + it('fetch next page when user clicks next', async () => { + const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()); + mountComponent({ resolver }); + + await waitForApolloRequestRender(); + + findPagination().vm.$emit('next'); + + expect(resolver).toHaveBeenCalledWith( + expect.objectContaining({ after: tagsPageInfo.endCursor }), + ); + }); + + it('fetch previous page when user clicks prev', async () => { + const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()); + mountComponent({ resolver }); + + await waitForApolloRequestRender(); + + findPagination().vm.$emit('prev'); + + expect(resolver).toHaveBeenCalledWith( + expect.objectContaining({ first: null, before: tagsPageInfo.startCursor }), + ); + }); }); describe('modal', () => { - it('exists', () => { + it('exists', async () => { mountComponent(); + + await waitForApolloRequestRender(); + expect(findDeleteModal().exists()).toBe(true); }); describe('cancel event', () => { - it('tracks cancel_delete', () => { + it('tracks cancel_delete', async () => { mountComponent(); + + await waitForApolloRequestRender(); + findDeleteModal().vm.$emit('cancel'); + expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', { label: 'registry_tag_delete', }); @@ -224,45 +305,57 @@ describe('Details Page', () => { }); describe('confirmDelete event', () => { + let mutationResolver; + + beforeEach(() => { + mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); + mountComponent({ mutationResolver }); + + return waitForApolloRequestRender(); + }); describe('when one item is selected to be deleted', () => { - beforeEach(() => { - mountComponent(); - findTagsList().vm.$emit('delete', { [store.state.tags[0].name]: true }); - }); + it('calls apollo mutation with the right parameters', async () => { + findTagsList().vm.$emit('delete', { [cleanTags[0].name]: true }); + + await wrapper.vm.$nextTick(); - it('dispatch requestDeleteTag with the right parameters', () => { findDeleteModal().vm.$emit('confirmDelete'); - expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTag', { - tag: store.state.tags[0], - }); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: [cleanTags[0].name] }), + ); }); }); describe('when more than one item is selected to be deleted', () => { - beforeEach(() => { - mountComponent(); - findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags)); - }); + it('calls apollo mutation with the right parameters', async () => { + findTagsList().vm.$emit('delete', { ...tagsArrayToSelectedTags(tagsMock) }); + + await wrapper.vm.$nextTick(); - it('dispatch requestDeleteTags with the right parameters', () => { findDeleteModal().vm.$emit('confirmDelete'); - expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTags', { - ids: store.state.tags.map(t => t.name), - }); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: tagsMock.map(t => t.name) }), + ); }); }); }); }); describe('Header', () => { - it('exists', () => { + it('exists', async () => { mountComponent(); + + await waitForApolloRequestRender(); expect(findDetailsHeader().exists()).toBe(true); }); - it('has the correct props', () => { + it('has the correct props', async () => { mountComponent(); - expect(findDetailsHeader().props()).toEqual({ imageName: imageDetailsMock.name }); + + await waitForApolloRequestRender(); + expect(findDetailsHeader().props()).toEqual({ imageName: containerRepositoryMock.name }); }); }); @@ -273,13 +366,15 @@ describe('Details Page', () => { }; const deleteAlertType = 'success_tag'; - it('exists', () => { + it('exists', async () => { mountComponent(); + + await waitForApolloRequestRender(); expect(findDeleteAlert().exists()).toBe(true); }); - it('has the correct props', () => { - store.commit(SET_INITIAL_STATE, { ...config }); + it('has the correct props', async () => { + store.commit('SET_INITIAL_STATE', { ...config }); mountComponent({ options: { data: () => ({ @@ -287,6 +382,9 @@ describe('Details Page', () => { }), }, }); + + await waitForApolloRequestRender(); + expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType }); }); }); @@ -298,30 +396,40 @@ describe('Details Page', () => { }; describe('when expiration_policy_started is not null', () => { + let resolver; + beforeEach(() => { - store.commit(SET_IMAGE_DETAILS, { - ...imageDetailsMock, - cleanup_policy_started_at: Date.now().toString(), - }); + resolver = jest.fn().mockResolvedValue( + graphQLImageDetailsMock({ + expirationPolicyStartedAt: Date.now().toString(), + }), + ); }); - it('exists', () => { - mountComponent(); + it('exists', async () => { + mountComponent({ resolver }); + + await waitForApolloRequestRender(); expect(findPartialCleanupAlert().exists()).toBe(true); }); - it('has the correct props', () => { - store.commit(SET_INITIAL_STATE, { ...config }); + it('has the correct props', async () => { + store.commit('SET_INITIAL_STATE', { ...config }); - mountComponent(); + mountComponent({ resolver }); + + await waitForApolloRequestRender(); expect(findPartialCleanupAlert().props()).toEqual({ ...config }); }); it('dismiss hides the component', async () => { - mountComponent(); + mountComponent({ resolver }); + + await waitForApolloRequestRender(); expect(findPartialCleanupAlert().exists()).toBe(true); + findPartialCleanupAlert().vm.$emit('dismiss'); await wrapper.vm.$nextTick(); @@ -331,11 +439,22 @@ describe('Details Page', () => { }); describe('when expiration_policy_started is null', () => { - it('the component is hidden', () => { + it('the component is hidden', async () => { mountComponent(); + await waitForApolloRequestRender(); expect(findPartialCleanupAlert().exists()).toBe(false); }); }); }); + + describe('Breadcrumb connection', () => { + it('when the details are fetched updates the name', async () => { + mountComponent(); + + await waitForApolloRequestRender(); + + expect(breadCrumbState.updateName).toHaveBeenCalledWith(containerRepositoryMock.name); + }); + }); }); |