diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-03 12:10:48 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-03 12:10:48 +0300 |
commit | f6a4346585dcc6cc564cb4974a0ea49d2ac0531d (patch) | |
tree | 9e9109c2442dbac93c7a38d60c093b56be9e0fa4 /spec/frontend/packages_and_registries | |
parent | bdfa808bfa4983d67a1ff54e4a3b7070be4d13eb (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/packages_and_registries')
3 files changed, 186 insertions, 76 deletions
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/package_versions_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/package_versions_list_spec.js index fc7f5c80d45..a700f42d367 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/details/package_versions_list_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/components/details/package_versions_list_spec.js @@ -1,5 +1,11 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlAlert } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; import { stubComponent } from 'helpers/stub_component'; +import waitForPromises from 'helpers/wait_for_promises'; import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue'; import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue'; import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue'; @@ -14,24 +20,26 @@ import { DELETE_PACKAGE_VERSIONS_TRACKING_ACTION, REQUEST_DELETE_PACKAGE_VERSION_TRACKING_ACTION, REQUEST_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION, + GRAPHQL_PAGE_SIZE, } from '~/packages_and_registries/package_registry/constants'; -import { packageData } from '../../mock_data'; +import getPackageVersionsQuery from '~/packages_and_registries/package_registry/graphql//queries/get_package_versions.query.graphql'; +import { + emptyPackageVersionsQuery, + packageVersionsQuery, + packageVersions, + pagination, +} from '../../mock_data'; + +Vue.use(VueApollo); describe('PackageVersionsList', () => { let wrapper; + let apolloProvider; const EmptySlotStub = { name: 'empty-slot-stub', template: '<div>empty message</div>' }; - const packageList = [ - packageData({ - name: 'version 1', - }), - packageData({ - id: 'gid://gitlab/Packages::Package/112', - name: 'version 2', - }), - ]; const uiElements = { + findAlert: () => wrapper.findComponent(GlAlert), findLoader: () => wrapper.findComponent(PackagesListLoader), findRegistryList: () => wrapper.findComponent(RegistryList), findEmptySlot: () => wrapper.findComponent(EmptySlotStub), @@ -40,12 +48,20 @@ describe('PackageVersionsList', () => { findDeletePackagesModal: () => wrapper.findComponent(DeleteModal), findPackageListDeleteModal: () => wrapper.findComponent(DeletePackageModal), }; - const mountComponent = (props) => { + + const mountComponent = ({ + props = {}, + resolver = jest.fn().mockResolvedValue(packageVersionsQuery()), + } = {}) => { + const requestHandlers = [[getPackageVersionsQuery, resolver]]; + apolloProvider = createMockApollo(requestHandlers); + wrapper = shallowMountExtended(PackageVersionsList, { + apolloProvider, propsData: { - versions: packageList, - pageInfo: {}, - isLoading: false, + packageId: packageVersionsQuery().data.package.id, + isMutationLoading: false, + count: packageVersions().length, ...props, }, stubs: { @@ -62,9 +78,13 @@ describe('PackageVersionsList', () => { }); }; + beforeEach(() => { + jest.spyOn(Sentry, 'captureException').mockImplementation(); + }); + describe('when list is loading', () => { beforeEach(() => { - mountComponent({ isLoading: true, versions: [] }); + mountComponent({ props: { isMutationLoading: true } }); }); it('displays loader', () => { expect(uiElements.findLoader().exists()).toBe(true); @@ -81,11 +101,24 @@ describe('PackageVersionsList', () => { it('does not display registry list', () => { expect(uiElements.findRegistryList().exists()).toBe(false); }); + + it('does not display alert', () => { + expect(uiElements.findAlert().exists()).toBe(false); + }); }); describe('when list is loaded and has no data', () => { - beforeEach(() => { - mountComponent({ isLoading: false, versions: [] }); + const resolver = jest.fn().mockResolvedValue(emptyPackageVersionsQuery); + beforeEach(async () => { + mountComponent({ + props: { isMutationLoading: false, count: 0 }, + resolver, + }); + await waitForPromises(); + }); + + it('skips graphql query', () => { + expect(resolver).not.toHaveBeenCalled(); }); it('displays empty slot message', () => { @@ -103,11 +136,44 @@ describe('PackageVersionsList', () => { it('does not display registry list', () => { expect(uiElements.findRegistryList().exists()).toBe(false); }); + + it('does not display alert', () => { + expect(uiElements.findAlert().exists()).toBe(false); + }); + }); + + describe('if load fails, alert', () => { + beforeEach(async () => { + mountComponent({ resolver: jest.fn().mockRejectedValue() }); + + await waitForPromises(); + }); + + it('is displayed', () => { + expect(uiElements.findAlert().exists()).toBe(true); + }); + + it('shows error message', () => { + expect(uiElements.findAlert().text()).toMatchInterpolatedText('Failed to load version data'); + }); + + it('is not dismissible', () => { + expect(uiElements.findAlert().props('dismissible')).toBe(false); + }); + + it('is of variant danger', () => { + expect(uiElements.findAlert().attributes('variant')).toBe('danger'); + }); + + it('error is logged in sentry', () => { + expect(Sentry.captureException).toHaveBeenCalled(); + }); }); describe('when list is loaded with data', () => { - beforeEach(() => { + beforeEach(async () => { mountComponent(); + await waitForPromises(); }); it('displays package registry list', () => { @@ -116,7 +182,7 @@ describe('PackageVersionsList', () => { it('binds the right props', () => { expect(uiElements.findRegistryList().props()).toMatchObject({ - items: packageList, + items: packageVersions(), pagination: {}, isLoading: false, hiddenDelete: true, @@ -125,16 +191,16 @@ describe('PackageVersionsList', () => { it('displays package version rows', () => { expect(uiElements.findAllListRow().exists()).toEqual(true); - expect(uiElements.findAllListRow()).toHaveLength(packageList.length); + expect(uiElements.findAllListRow()).toHaveLength(packageVersions().length); }); it('binds the correct props', () => { expect(uiElements.findAllListRow().at(0).props()).toMatchObject({ - packageEntity: expect.objectContaining(packageList[0]), + packageEntity: expect.objectContaining(packageVersions()[0]), }); expect(uiElements.findAllListRow().at(1).props()).toMatchObject({ - packageEntity: expect.objectContaining(packageList[1]), + packageEntity: expect.objectContaining(packageVersions()[1]), }); }); @@ -148,40 +214,52 @@ describe('PackageVersionsList', () => { }); describe('when user interacts with pagination', () => { - beforeEach(() => { - mountComponent({ pageInfo: { hasNextPage: true } }); + const resolver = jest.fn().mockResolvedValue(packageVersionsQuery()); + + beforeEach(async () => { + mountComponent({ resolver }); + await waitForPromises(); }); - it('emits prev-page event when registry list emits prev event', () => { - uiElements.findRegistryList().vm.$emit('prev-page'); + it('when list emits next-page fetches the next set of records', async () => { + uiElements.findRegistryList().vm.$emit('next-page'); + await waitForPromises(); - expect(wrapper.emitted('prev-page')).toHaveLength(1); + expect(resolver).toHaveBeenLastCalledWith( + expect.objectContaining({ after: pagination().endCursor, first: GRAPHQL_PAGE_SIZE }), + ); }); - it('emits next-page when registry list emits next event', () => { - uiElements.findRegistryList().vm.$emit('next-page'); + it('when list emits prev-page fetches the prev set of records', async () => { + uiElements.findRegistryList().vm.$emit('prev-page'); + await waitForPromises(); - expect(wrapper.emitted('next-page')).toHaveLength(1); + expect(resolver).toHaveBeenLastCalledWith( + expect.objectContaining({ before: pagination().startCursor, last: GRAPHQL_PAGE_SIZE }), + ); }); }); describe.each` description | finderFunction | deletePayload - ${'when the user can destroy the package'} | ${uiElements.findListRow} | ${packageList[0]} - ${'when the user can bulk destroy packages and deletes only one package'} | ${uiElements.findRegistryList} | ${[packageList[0]]} + ${'when the user can destroy the package'} | ${uiElements.findListRow} | ${packageVersions()[0]} + ${'when the user can bulk destroy packages and deletes only one package'} | ${uiElements.findRegistryList} | ${[packageVersions()[0]]} `('$description', ({ finderFunction, deletePayload }) => { let eventSpy; const category = 'UI::NpmPackages'; const { findPackageListDeleteModal } = uiElements; - beforeEach(() => { + beforeEach(async () => { eventSpy = jest.spyOn(Tracking, 'event'); - mountComponent({ canDestroy: true }); + mountComponent({ props: { canDestroy: true } }); + await waitForPromises(); finderFunction().vm.$emit('delete', deletePayload); }); it('passes itemToBeDeleted to the modal', () => { - expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual(packageList[0]); + expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual( + packageVersions()[0], + ); }); it('requesting delete tracks the right action', () => { @@ -198,7 +276,7 @@ describe('PackageVersionsList', () => { }); it('emits delete when modal confirms', () => { - expect(wrapper.emitted('delete')[0][0]).toEqual([packageList[0]]); + expect(wrapper.emitted('delete')[0][0]).toEqual([packageVersions()[0]]); }); it('tracks the right action', () => { @@ -231,14 +309,15 @@ describe('PackageVersionsList', () => { let eventSpy; const { findDeletePackagesModal, findRegistryList } = uiElements; - beforeEach(() => { + beforeEach(async () => { eventSpy = jest.spyOn(Tracking, 'event'); - mountComponent({ canDestroy: true }); + mountComponent({ props: { canDestroy: true } }); + await waitForPromises(); }); it('binds the right props', () => { expect(uiElements.findRegistryList().props()).toMatchObject({ - items: packageList, + items: packageVersions(), pagination: {}, isLoading: false, hiddenDelete: false, @@ -248,11 +327,13 @@ describe('PackageVersionsList', () => { describe('upon deletion', () => { beforeEach(() => { - findRegistryList().vm.$emit('delete', packageList); + findRegistryList().vm.$emit('delete', packageVersions()); }); it('passes itemsToBeDeleted to the modal', () => { - expect(findDeletePackagesModal().props('itemsToBeDeleted')).toStrictEqual(packageList); + expect(findDeletePackagesModal().props('itemsToBeDeleted')).toStrictEqual( + packageVersions(), + ); expect(wrapper.emitted('delete')).toBeUndefined(); }); @@ -270,7 +351,7 @@ describe('PackageVersionsList', () => { }); it('emits delete event', () => { - expect(wrapper.emitted('delete')[0]).toEqual([packageList]); + expect(wrapper.emitted('delete')[0]).toEqual([packageVersions()]); }); it('tracks the right action', () => { diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js index 19c098e1f82..d568cfe6221 100644 --- a/spec/frontend/packages_and_registries/package_registry/mock_data.js +++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js @@ -109,6 +109,7 @@ export const packageVersions = () => [ id: 'gid://gitlab/Packages::Package/243', name: '@gitlab-org/package-15', status: 'DEFAULT', + packageType: 'NPM', canDestroy: true, tags: { nodes: packageTags() }, version: '1.0.1', @@ -120,6 +121,7 @@ export const packageVersions = () => [ id: 'gid://gitlab/Packages::Package/244', name: '@gitlab-org/package-15', status: 'DEFAULT', + packageType: 'NPM', canDestroy: true, tags: { nodes: packageTags() }, version: '1.0.2', @@ -130,7 +132,7 @@ export const packageVersions = () => [ export const packageData = (extend) => ({ __typename: 'Package', - id: 'gid://gitlab/Packages::Package/111', + id: 'gid://gitlab/Packages::Package/1', canDestroy: true, name: '@gitlab-org/package-15', packageType: 'NPM', @@ -244,14 +246,6 @@ export const packageDetailsQuery = (extendPackage) => ({ }, versions: { count: packageVersions().length, - nodes: packageVersions(), - pageInfo: { - hasNextPage: true, - hasPreviousPage: false, - endCursor: 'endCursor', - startCursor: 'startCursor', - }, - __typename: 'PackageConnection', }, dependencyLinks: { nodes: dependencyLinks(), @@ -298,6 +292,41 @@ export const packageMetadataQuery = (packageType) => { }; }; +export const packageVersionsQuery = (versions = packageVersions()) => ({ + data: { + package: { + id: 'gid://gitlab/Packages::Package/111', + versions: { + count: versions.length, + nodes: versions, + pageInfo: pagination(), + __typename: 'PackageConnection', + }, + __typename: 'PackageDetailsType', + }, + }, +}); + +export const emptyPackageVersionsQuery = { + data: { + package: { + id: 'gid://gitlab/Packages::Package/111', + versions: { + count: 0, + nodes: [], + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + endCursor: 'endCursor', + startCursor: 'startCursor', + }, + __typename: 'PackageConnection', + }, + __typename: 'PackageDetailsType', + }, + }, +}; + export const packagesDestroyMutation = () => ({ data: { destroyPackages: { diff --git a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js index 544591ff2ab..ecc82b73fed 100644 --- a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js @@ -33,6 +33,7 @@ import { import destroyPackageFilesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_files.mutation.graphql'; import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql'; +import getPackageVersionsQuery from '~/packages_and_registries/package_registry/graphql//queries/get_package_versions.query.graphql'; import { packageDetailsQuery, packageData, @@ -42,12 +43,13 @@ import { packageFiles, packageDestroyFilesMutation, packageDestroyFilesMutationError, - pagination, } from '../mock_data'; jest.mock('~/alert'); useMockLocationHelper(); +Vue.use(VueApollo); + describe('PackagesApp', () => { let wrapper; let apolloProvider; @@ -57,7 +59,7 @@ describe('PackagesApp', () => { }; const provide = { - packageId: '111', + packageId: '1', emptyListIllustration: 'svgPath', projectListUrl: 'projectListUrl', groupListUrl: 'groupListUrl', @@ -73,8 +75,6 @@ describe('PackagesApp', () => { filesDeleteMutationResolver = jest.fn().mockResolvedValue(packageDestroyFilesMutation()), routeId = '1', } = {}) { - Vue.use(VueApollo); - const requestHandlers = [ [getPackageDetails, resolver], [destroyPackageFilesMutation, filesDeleteMutationResolver], @@ -475,6 +475,8 @@ describe('PackagesApp', () => { await doDeleteFiles(); + expect(resolver).toHaveBeenCalledTimes(2); + expect(createAlert).toHaveBeenCalledWith( expect.objectContaining({ message: DELETE_PACKAGE_FILES_SUCCESS_MESSAGE, @@ -565,8 +567,6 @@ describe('PackagesApp', () => { packageDetailsQuery({ versions: { count: 0, - nodes: [], - pageInfo: pagination({ hasNextPage: false, hasPreviousPage: false }), }, }), ), @@ -582,55 +582,55 @@ describe('PackagesApp', () => { }); it('binds the correct props', async () => { - const versionNodes = packageVersions(); createComponent(); await waitForPromises(); expect(findVersionsList().props()).toMatchObject({ canDestroy: true, - versions: expect.arrayContaining(versionNodes), + count: packageVersions().length, + isMutationLoading: false, + packageId: 'gid://gitlab/Packages::Package/1', }); }); describe('delete packages', () => { - it('exists and has the correct props', async () => { + beforeEach(async () => { createComponent(); - await waitForPromises(); - - expect(findDeletePackages().props()).toMatchObject({ - refetchQueries: [{ query: getPackageDetails, variables: {} }], - showSuccessAlert: true, - }); }); - it('deletePackages is bound to package-versions-list delete event', async () => { - createComponent(); - - await waitForPromises(); + it('exists and has the correct props', () => { + expect(findDeletePackages().props('showSuccessAlert')).toBe(true); + expect(findDeletePackages().props('refetchQueries')).toEqual([ + { + query: getPackageVersionsQuery, + variables: { + first: 20, + id: 'gid://gitlab/Packages::Package/1', + }, + }, + ]); + }); + it('deletePackages is bound to package-versions-list delete event', () => { findVersionsList().vm.$emit('delete', [{ id: 1 }]); expect(findDeletePackages().emitted('start')).toEqual([[]]); }); it('start and end event set loading correctly', async () => { - createComponent(); - - await waitForPromises(); - findDeletePackages().vm.$emit('start'); await nextTick(); - expect(findVersionsList().props('isLoading')).toBe(true); + expect(findVersionsList().props('isMutationLoading')).toBe(true); findDeletePackages().vm.$emit('end'); await nextTick(); - expect(findVersionsList().props('isLoading')).toBe(false); + expect(findVersionsList().props('isMutationLoading')).toBe(false); }); }); }); |