diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-31 18:08:50 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-31 18:08:50 +0300 |
commit | ccab6fb4df8bc12220334618e56d911c4d0e447c (patch) | |
tree | a8c7bb66fb4c36c97e7e1b4f27744fcd93c3bcf3 /spec/frontend/packages_and_registries | |
parent | 820c5f6d5c816ba4b742f2ae2e08cc548314531a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/packages_and_registries')
2 files changed, 273 insertions, 135 deletions
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js index 09d0370efbf..0cbb9eab018 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js @@ -4,13 +4,15 @@ import { GlEmptyState } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; - +import Tracking from '~/tracking'; import component from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue'; import TagsListRow from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue'; import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue'; import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue'; import getContainerRepositoryTagsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql'; +import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql'; + import { GRAPHQL_PAGE_SIZE, NO_TAGS_TITLE, @@ -19,7 +21,13 @@ import { NO_TAGS_MATCHING_FILTERS_DESCRIPTION, } from '~/packages_and_registries/container_registry/explorer/constants/index'; import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants'; -import { tagsMock, imageTagsMock, tagsPageInfo } from '../../mock_data'; +import { + graphQLDeleteImageRepositoryTagsMock, + tagsMock, + imageTagsMock, + tagsPageInfo, +} from '../../mock_data'; +import { DeleteModal } from '../../stubs'; describe('Tags List', () => { let wrapper; @@ -31,6 +39,7 @@ describe('Tags List', () => { noContainersImage: 'noContainersImage', }; + const findDeleteModal = () => wrapper.findComponent(DeleteModal); const findPersistedSearch = () => wrapper.findComponent(PersistedSearch); const findTagsListRow = () => wrapper.findAllComponents(TagsListRow); const findRegistryList = () => wrapper.findComponent(RegistryList); @@ -42,20 +51,23 @@ describe('Tags List', () => { }; const waitForApolloRequestRender = async () => { + fireFirstSortUpdate(); await waitForPromises(); - await nextTick(); }; - const mountComponent = ({ propsData = { isMobile: false, id: 1 } } = {}) => { + const mountComponent = ({ propsData = { isMobile: false, id: 1 }, mutationResolver } = {}) => { Vue.use(VueApollo); - const requestHandlers = [[getContainerRepositoryTagsQuery, resolver]]; + const requestHandlers = [ + [getContainerRepositoryTagsQuery, resolver], + [deleteContainerRepositoryTagsMutation, mutationResolver], + ]; apolloProvider = createMockApollo(requestHandlers); wrapper = shallowMount(component, { apolloProvider, propsData, - stubs: { RegistryList }, + stubs: { RegistryList, DeleteModal }, provide() { return { config: defaultConfig, @@ -66,12 +78,12 @@ describe('Tags List', () => { beforeEach(() => { resolver = jest.fn().mockResolvedValue(imageTagsMock()); + jest.spyOn(Tracking, 'event'); }); describe('registry list', () => { beforeEach(async () => { mountComponent(); - fireFirstSortUpdate(); await waitForApolloRequestRender(); }); @@ -126,11 +138,46 @@ describe('Tags List', () => { }); }); - it('emits a delete event when list emits delete', () => { - const eventPayload = 'foo'; - findRegistryList().vm.$emit('delete', eventPayload); + describe('delete event', () => { + describe('single item', () => { + beforeEach(() => { + findRegistryList().vm.$emit('delete', [tags[0]]); + }); + + it('opens the modal', () => { + expect(DeleteModal.methods.show).toHaveBeenCalled(); + }); + + it('sets modal props', () => { + expect(findDeleteModal().props('itemsToBeDeleted')).toMatchObject([tags[0]]); + }); + + it('tracks a single delete event', () => { + expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { + label: 'registry_tag_delete', + }); + }); + }); + + describe('multiple items', () => { + beforeEach(() => { + findRegistryList().vm.$emit('delete', tags); + }); + + it('opens the modal', () => { + expect(DeleteModal.methods.show).toHaveBeenCalled(); + }); - expect(wrapper.emitted('delete')).toEqual([[eventPayload]]); + it('sets modal props', () => { + expect(findDeleteModal().props('itemsToBeDeleted')).toMatchObject(tags); + }); + + it('tracks multiple delete event', () => { + expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { + label: 'bulk_registry_tag_delete', + }); + }); + }); }); }); }); @@ -138,7 +185,6 @@ describe('Tags List', () => { describe('list rows', () => { it('one row exist for each tag', async () => { mountComponent(); - fireFirstSortUpdate(); await waitForApolloRequestRender(); @@ -147,7 +193,6 @@ describe('Tags List', () => { it('the correct props are bound to it', async () => { mountComponent({ propsData: { disabled: true, id: 1 } }); - fireFirstSortUpdate(); await waitForApolloRequestRender(); @@ -162,7 +207,6 @@ describe('Tags List', () => { describe('events', () => { it('select event update the selected items', async () => { mountComponent(); - fireFirstSortUpdate(); await waitForApolloRequestRender(); findTagsListRow().at(0).vm.$emit('select'); @@ -172,13 +216,44 @@ describe('Tags List', () => { expect(findTagsListRow().at(0).attributes('selected')).toBe('true'); }); - it('delete event emit a delete event', async () => { - mountComponent(); - fireFirstSortUpdate(); - await waitForApolloRequestRender(); + describe('delete event', () => { + let mutationResolver; + + beforeEach(async () => { + mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); + resolver = jest.fn().mockResolvedValue(imageTagsMock()); + mountComponent({ mutationResolver }); - findTagsListRow().at(0).vm.$emit('delete'); - expect(wrapper.emitted('delete')[0][0][0].name).toBe(tags[0].name); + await waitForApolloRequestRender(); + findTagsListRow().at(0).vm.$emit('delete'); + }); + + it('opens the modal', () => { + expect(DeleteModal.methods.show).toHaveBeenCalled(); + }); + + it('tracks a single delete event', () => { + expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { + label: 'registry_tag_delete', + }); + }); + + it('confirmDelete event calls apollo mutation with the right parameters and refetches the tags list query', async () => { + findDeleteModal().vm.$emit('confirmDelete'); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: [tags[0].name] }), + ); + + await waitForPromises(); + + expect(resolver).toHaveBeenLastCalledWith({ + first: GRAPHQL_PAGE_SIZE, + name: '', + sort: 'NAME_ASC', + id: '1', + }); + }); }); }); }); @@ -187,7 +262,6 @@ describe('Tags List', () => { it('sets registry list hiddenDelete prop to true', async () => { resolver = jest.fn().mockResolvedValue(imageTagsMock({ canDelete: false })); mountComponent(); - fireFirstSortUpdate(); await waitForApolloRequestRender(); expect(findRegistryList().props('hiddenDelete')).toBe(true); @@ -198,7 +272,6 @@ describe('Tags List', () => { beforeEach(async () => { resolver = jest.fn().mockResolvedValue(imageTagsMock({ nodes: [] })); mountComponent(); - fireFirstSortUpdate(); await waitForApolloRequestRender(); }); @@ -225,7 +298,7 @@ describe('Tags List', () => { filters: [{ type: FILTERED_SEARCH_TERM, value: { data: 'foo' } }], }); - await waitForApolloRequestRender(); + await waitForPromises(); expect(findEmptyState().props()).toMatchObject({ svgPath: defaultConfig.noContainersImage, @@ -236,6 +309,175 @@ describe('Tags List', () => { }); }); + describe('modal', () => { + it('exists', async () => { + mountComponent(); + await waitForApolloRequestRender(); + + expect(findDeleteModal().exists()).toBe(true); + }); + + describe('cancel event', () => { + it('tracks cancel_delete', async () => { + mountComponent(); + await waitForApolloRequestRender(); + + findDeleteModal().vm.$emit('cancel'); + + expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', { + label: 'registry_tag_delete', + }); + }); + }); + + describe('confirmDelete event', () => { + let mutationResolver; + + describe('when mutation', () => { + beforeEach(() => { + mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); + mountComponent({ mutationResolver }); + + return waitForApolloRequestRender(); + }); + + it('is started renders loader', async () => { + findRegistryList().vm.$emit('delete', [tags[0]]); + + findDeleteModal().vm.$emit('confirmDelete'); + await nextTick(); + + expect(findTagsLoader().exists()).toBe(true); + expect(findTagsListRow().exists()).toBe(false); + }); + + it('ends, loader is hidden', async () => { + findRegistryList().vm.$emit('delete', [tags[0]]); + + findDeleteModal().vm.$emit('confirmDelete'); + await waitForPromises(); + + expect(findTagsLoader().exists()).toBe(false); + expect(findTagsListRow().exists()).toBe(true); + }); + }); + + describe.each([ + { + description: 'rejection', + mutationMock: jest.fn().mockRejectedValue(), + }, + { + description: 'error', + mutationMock: jest.fn().mockResolvedValue({ + data: { + destroyContainerRepositoryTags: { + errors: [new Error()], + }, + }, + }), + }, + ])('when mutation fails with $description', ({ mutationMock }) => { + beforeEach(() => { + mutationResolver = mutationMock; + mountComponent({ mutationResolver }); + + return waitForApolloRequestRender(); + }); + + it('when one item is selected to be deleted calls apollo mutation with the right parameters and emits delete event with right arguments', async () => { + findRegistryList().vm.$emit('delete', [tags[0]]); + + resolver.mockClear(); + + findDeleteModal().vm.$emit('confirmDelete'); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: [tags[0].name] }), + ); + + expect(resolver).not.toHaveBeenCalled(); + + await waitForPromises(); + + expect(wrapper.emitted('delete')).toHaveLength(1); + expect(wrapper.emitted('delete')[0][0]).toEqual('danger_tag'); + }); + + it('when more than one item is selected to be deleted calls apollo mutation with the right parameters and emits delete event with right arguments', async () => { + findRegistryList().vm.$emit('delete', tagsMock); + resolver.mockClear(); + + findDeleteModal().vm.$emit('confirmDelete'); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }), + ); + + expect(resolver).not.toHaveBeenCalled(); + + await waitForPromises(); + + expect(wrapper.emitted('delete')).toHaveLength(1); + expect(wrapper.emitted('delete')[0][0]).toEqual('danger_tags'); + }); + }); + + describe('when mutation is successful', () => { + beforeEach(() => { + mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); + mountComponent({ mutationResolver }); + + return waitForApolloRequestRender(); + }); + + it('and one item is selected to be deleted calls apollo mutation with the right parameters and refetches the tags list query', async () => { + findRegistryList().vm.$emit('delete', [tags[0]]); + + findDeleteModal().vm.$emit('confirmDelete'); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: [tags[0].name] }), + ); + + expect(resolver).toHaveBeenLastCalledWith({ + first: GRAPHQL_PAGE_SIZE, + name: '', + sort: 'NAME_ASC', + id: '1', + }); + + await waitForPromises(); + + expect(wrapper.emitted('delete')).toHaveLength(1); + expect(wrapper.emitted('delete')[0][0]).toEqual('success_tag'); + }); + + it('and more than one item is selected to be deleted calls apollo mutation with the right parameters and refetches the tags list query', async () => { + findRegistryList().vm.$emit('delete', tagsMock); + + findDeleteModal().vm.$emit('confirmDelete'); + + expect(mutationResolver).toHaveBeenCalledWith( + expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }), + ); + + expect(resolver).toHaveBeenLastCalledWith({ + first: GRAPHQL_PAGE_SIZE, + name: '', + sort: 'NAME_ASC', + id: '1', + }); + + await waitForPromises(); + + expect(wrapper.emitted('delete')).toHaveLength(1); + expect(wrapper.emitted('delete')[0][0]).toEqual('success_tags'); + }); + }); + }); + }); + describe('loading state', () => { it.each` isImageLoading | queryExecuting | loadingVisible @@ -247,7 +489,6 @@ describe('Tags List', () => { 'when the isImageLoading is $isImageLoading, and is $queryExecuting that the query is still executing is $loadingVisible that the loader is shown', async ({ isImageLoading, queryExecuting, loadingVisible }) => { mountComponent({ propsData: { isImageLoading, isMobile: false, id: 1 } }); - fireFirstSortUpdate(); if (!queryExecuting) { await waitForApolloRequestRender(); } diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js index 888c3e5bffa..7fed81acead 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js @@ -22,22 +22,15 @@ import { MISSING_OR_DELETED_IMAGE_TITLE, MISSING_OR_DELETED_IMAGE_MESSAGE, } from '~/packages_and_registries/container_registry/explorer/constants'; -import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql'; import getContainerRepositoryDetailsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_details.query.graphql'; -import getContainerRepositoryTagsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql'; -import getContainerRepositoriesDetails from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repositories_details.query.graphql'; import component from '~/packages_and_registries/container_registry/explorer/pages/details.vue'; import Tracking from '~/tracking'; import { graphQLImageDetailsMock, - graphQLDeleteImageRepositoryTagsMock, - graphQLProjectImageRepositoriesDetailsMock, containerRepositoryMock, graphQLEmptyImageDetailsMock, - tagsMock, - imageTagsMock, } from '../mock_data'; import { DeleteModal } from '../stubs'; @@ -69,13 +62,6 @@ describe('Details Page', () => { isGroupPage: false, }; - 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 nextTick(); @@ -83,20 +69,12 @@ describe('Details Page', () => { const mountComponent = ({ resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()), - mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock), - tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock())), - detailsResolver = jest.fn().mockResolvedValue(graphQLProjectImageRepositoriesDetailsMock), options, config = defaultConfig, } = {}) => { Vue.use(VueApollo); - const requestHandlers = [ - [getContainerRepositoryDetailsQuery, resolver], - [deleteContainerRepositoryTagsMutation, mutationResolver], - [getContainerRepositoryTagsQuery, tagsResolver], - [getContainerRepositoriesDetails, detailsResolver], - ]; + const requestHandlers = [[getContainerRepositoryDetailsQuery, resolver]]; apolloProvider = createMockApollo(requestHandlers); @@ -184,50 +162,6 @@ describe('Details Page', () => { isMobile: false, }); }); - - describe('deleteEvent', () => { - describe('single item', () => { - let tagToBeDeleted; - beforeEach(async () => { - mountComponent(); - - await waitForApolloRequestRender(); - - [tagToBeDeleted] = cleanTags; - findTagsList().vm.$emit('delete', [tagToBeDeleted]); - }); - - it('open the modal', async () => { - expect(DeleteModal.methods.show).toHaveBeenCalled(); - }); - - it('tracks a single delete event', () => { - expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { - label: 'registry_tag_delete', - }); - }); - }); - - describe('multiple items', () => { - beforeEach(async () => { - mountComponent(); - - await waitForApolloRequestRender(); - - findTagsList().vm.$emit('delete', cleanTags); - }); - - it('open the modal', () => { - expect(DeleteModal.methods.show).toHaveBeenCalled(); - }); - - it('tracks a single delete event', () => { - expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', { - label: 'bulk_registry_tag_delete', - }); - }); - }); - }); }); describe('modal', () => { @@ -248,61 +182,24 @@ describe('Details Page', () => { findDeleteModal().vm.$emit('cancel'); expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', { - label: 'registry_tag_delete', + label: 'registry_image_delete', }); }); }); - describe('confirmDelete event', () => { - let mutationResolver; - let tagsResolver; - let detailsResolver; - + describe('tags list delete event', () => { beforeEach(() => { - mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); - tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock())); - detailsResolver = jest.fn().mockResolvedValue(graphQLProjectImageRepositoriesDetailsMock); - mountComponent({ mutationResolver, tagsResolver, detailsResolver }); + mountComponent(); return waitForApolloRequestRender(); }); - describe('when one item is selected to be deleted', () => { - it('calls apollo mutation with the right parameters and refetches the tags list query', async () => { - findTagsList().vm.$emit('delete', [cleanTags[0]]); - - await nextTick(); - - findDeleteModal().vm.$emit('confirmDelete'); - - expect(mutationResolver).toHaveBeenCalledWith( - expect.objectContaining({ tagNames: [cleanTags[0].name] }), - ); - - await waitForPromises(); - - expect(tagsResolver).toHaveBeenCalled(); - expect(detailsResolver).toHaveBeenCalled(); - }); - }); - - describe('when more than one item is selected to be deleted', () => { - it('calls apollo mutation with the right parameters and refetches the tags list query', async () => { - findTagsList().vm.$emit('delete', tagsMock); - - await nextTick(); + it('sets delete alert modal deleteAlertType value', async () => { + findTagsList().vm.$emit('delete', 'success_tag'); - findDeleteModal().vm.$emit('confirmDelete'); - - expect(mutationResolver).toHaveBeenCalledWith( - expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }), - ); - - await waitForPromises(); + await nextTick(); - expect(tagsResolver).toHaveBeenCalled(); - expect(detailsResolver).toHaveBeenCalled(); - }); + expect(findDeleteAlert().props('deleteAlertType')).toBe('success_tag'); }); }); }); |