diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /spec/frontend/packages_and_registries | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) | |
download | gitlab-ce-3cccd102ba543e02725d247893729e5c73b38295.tar.gz |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'spec/frontend/packages_and_registries')
22 files changed, 800 insertions, 307 deletions
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js index 6d7bf528495..ad67128502a 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js @@ -1,7 +1,7 @@ -import { GlButton } from '@gitlab/ui'; +import { GlButton, GlTooltip, GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import component from '~/packages_and_registries/container_registry/explorer/components/delete_button.vue'; +import { LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION } from '~/packages_and_registries/container_registry/explorer/constants/list'; describe('delete_button', () => { let wrapper; @@ -12,6 +12,7 @@ describe('delete_button', () => { }; const findButton = () => wrapper.find(GlButton); + const findTooltip = () => wrapper.find(GlTooltip); const mountComponent = (props) => { wrapper = shallowMount(component, { @@ -19,8 +20,9 @@ describe('delete_button', () => { ...defaultProps, ...props, }, - directives: { - GlTooltip: createMockDirective(), + stubs: { + GlTooltip, + GlSprintf, }, }); }; @@ -33,41 +35,50 @@ describe('delete_button', () => { describe('tooltip', () => { it('the title is controlled by tooltipTitle prop', () => { mountComponent(); - const tooltip = getBinding(wrapper.element, 'gl-tooltip'); + const tooltip = findTooltip(); expect(tooltip).toBeDefined(); - expect(tooltip.value.title).toBe(defaultProps.tooltipTitle); + expect(tooltip.text()).toBe(defaultProps.tooltipTitle); }); it('is disabled when tooltipTitle is disabled', () => { mountComponent({ tooltipDisabled: true }); - const tooltip = getBinding(wrapper.element, 'gl-tooltip'); - expect(tooltip.value.disabled).toBe(true); + expect(findTooltip().props('disabled')).toBe(true); }); - describe('button', () => { - it('exists', () => { - mountComponent(); - expect(findButton().exists()).toBe(true); + it('works with a link', () => { + mountComponent({ + tooltipTitle: LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION, + tooltipLink: 'foo', }); + expect(findTooltip().text()).toMatchInterpolatedText( + LIST_DELETE_BUTTON_DISABLED_FOR_MIGRATION, + ); + }); + }); - it('has the correct props/attributes bound', () => { - mountComponent({ disabled: true }); - expect(findButton().attributes()).toMatchObject({ - 'aria-label': 'Foo title', - icon: 'remove', - title: 'Foo title', - variant: 'danger', - disabled: 'true', - category: 'secondary', - }); - }); + describe('button', () => { + it('exists', () => { + mountComponent(); + expect(findButton().exists()).toBe(true); + }); - it('emits a delete event', () => { - mountComponent(); - expect(wrapper.emitted('delete')).toEqual(undefined); - findButton().vm.$emit('click'); - expect(wrapper.emitted('delete')).toEqual([[]]); + it('has the correct props/attributes bound', () => { + mountComponent({ disabled: true }); + expect(findButton().attributes()).toMatchObject({ + 'aria-label': 'Foo title', + icon: 'remove', + title: 'Foo title', + variant: 'danger', + disabled: 'true', + category: 'secondary', }); }); + + it('emits a delete event', () => { + mountComponent(); + expect(wrapper.emitted('delete')).toEqual(undefined); + findButton().vm.$emit('click'); + expect(wrapper.emitted('delete')).toEqual([[]]); + }); }); }); diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js index 411bef54e40..690d827ec67 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js @@ -10,6 +10,7 @@ import { LIST_DELETE_BUTTON_DISABLED, REMOVE_REPOSITORY_LABEL, IMAGE_DELETE_SCHEDULED_STATUS, + IMAGE_MIGRATING_STATE, SCHEDULED_STATUS, ROOT_IMAGE_TEXT, } from '~/packages_and_registries/container_registry/explorer/constants'; @@ -41,6 +42,9 @@ describe('Image List Row', () => { item, ...props, }, + provide: { + config: {}, + }, directives: { GlTooltip: createMockDirective(), }, @@ -178,6 +182,12 @@ describe('Image List Row', () => { expect(findDeleteBtn().props('disabled')).toBe(state); }, ); + + it('is disabled when migrationState is importing', () => { + mountComponent({ item: { ...item, migrationState: IMAGE_MIGRATING_STATE } }); + + expect(findDeleteBtn().props('disabled')).toBe(true); + }); }); describe('tags count', () => { diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js index c91a9c0f0fb..7d09c09d03b 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js @@ -1,4 +1,4 @@ -import { GlSprintf } from '@gitlab/ui'; +import { GlSprintf, GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; import Component from '~/packages_and_registries/container_registry/explorer/components/list_page/registry_header.vue'; @@ -6,6 +6,7 @@ import { CONTAINER_REGISTRY_TITLE, LIST_INTRO_TEXT, EXPIRATION_POLICY_DISABLED_TEXT, + SET_UP_CLEANUP, } from '~/packages_and_registries/container_registry/explorer/constants'; import TitleArea from '~/vue_shared/components/registry/title_area.vue'; @@ -21,6 +22,7 @@ describe('registry_header', () => { const findCommandsSlot = () => wrapper.find('[data-testid="commands-slot"]'); const findImagesCountSubHeader = () => wrapper.find('[data-testid="images-count"]'); const findExpirationPolicySubHeader = () => wrapper.find('[data-testid="expiration-policy"]'); + const findSetupCleanUpLink = () => wrapper.findComponent(GlLink); const mountComponent = async (propsData, slots) => { wrapper = shallowMount(Component, { @@ -88,6 +90,7 @@ describe('registry_header', () => { }); const text = findExpirationPolicySubHeader(); + expect(text.exists()).toBe(true); expect(text.props()).toMatchObject({ text: EXPIRATION_POLICY_DISABLED_TEXT, @@ -100,12 +103,17 @@ describe('registry_header', () => { await mountComponent({ expirationPolicy: { enabled: true }, expirationPolicyHelpPagePath: 'foo', + showCleanupPolicyLink: true, imagesCount: 1, }); const text = findExpirationPolicySubHeader(); + const cleanupLink = findSetupCleanUpLink(); + expect(text.exists()).toBe(true); expect(text.props('text')).toBe('Expiration policy will run in '); + expect(cleanupLink.exists()).toBe(true); + expect(cleanupLink.text()).toBe(SET_UP_CLEANUP); }); it('when the expiration policy is completely disabled', async () => { await mountComponent({ diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js index fda1db4b7e1..7e6f88fe5bc 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/mock_data.js @@ -5,6 +5,7 @@ export const imagesListResponse = [ name: 'rails-12009', path: 'gitlab-org/gitlab-test/rails-12009', status: null, + migrationState: 'default', location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-12009', canDelete: true, createdAt: '2020-11-03T13:29:21Z', @@ -17,6 +18,7 @@ export const imagesListResponse = [ name: 'rails-20572', path: 'gitlab-org/gitlab-test/rails-20572', status: null, + migrationState: 'default', location: '0.0.0.0:5000/gitlab-org/gitlab-test/rails-20572', canDelete: true, createdAt: '2020-09-21T06:57:43Z', diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js index da4bfcde217..79403d29d18 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/list_spec.js @@ -6,7 +6,6 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql'; -import CleanupPolicyEnabledAlert from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue'; import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants'; import DeleteImage from '~/packages_and_registries/container_registry/explorer/components/delete_image.vue'; import CliCommands from '~/packages_and_registries/shared/components/cli_commands.vue'; @@ -58,7 +57,6 @@ describe('List Page', () => { const findPersistedSearch = () => wrapper.findComponent(PersistedSearch); const findEmptySearchMessage = () => wrapper.find('[data-testid="emptySearch"]'); const findDeleteImage = () => wrapper.findComponent(DeleteImage); - const findCleanupAlert = () => wrapper.findComponent(CleanupPolicyEnabledAlert); const fireFirstSortUpdate = () => { findPersistedSearch().vm.$emit('update', { sort: 'UPDATED_DESC', filters: [] }); @@ -511,33 +509,4 @@ describe('List Page', () => { testTrackingCall('confirm_delete'); }); }); - - describe('cleanup is on alert', () => { - it('exist when showCleanupPolicyOnAlert is true and has the correct props', async () => { - mountComponent({ - config: { - showCleanupPolicyOnAlert: true, - projectPath: 'foo', - isGroupPage: false, - cleanupPoliciesSettingsPath: 'bar', - }, - }); - - await waitForApolloRequestRender(); - - expect(findCleanupAlert().exists()).toBe(true); - expect(findCleanupAlert().props()).toMatchObject({ - projectPath: 'foo', - cleanupPoliciesSettingsPath: 'bar', - }); - }); - - it('is hidden when showCleanupPolicyOnAlert is false', async () => { - mountComponent(); - - await waitForApolloRequestRender(); - - expect(findCleanupAlert().exists()).toBe(false); - }); - }); }); diff --git a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js index 79894e25889..dbe9793fb8c 100644 --- a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js +++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js @@ -1,19 +1,26 @@ import { + GlAlert, + GlDropdown, + GlDropdownItem, GlFormInputGroup, GlFormGroup, + GlModal, GlSkeletonLoader, GlSprintf, GlEmptyState, } from '@gitlab/ui'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import MockAdapter from 'axios-mock-adapter'; import createMockApollo from 'helpers/mock_apollo_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { stripTypenames } from 'helpers/graphql_helpers'; import waitForPromises from 'helpers/wait_for_promises'; import { GRAPHQL_PAGE_SIZE } from '~/packages_and_registries/dependency_proxy/constants'; +import axios from '~/lib/utils/axios_utils'; import DependencyProxyApp from '~/packages_and_registries/dependency_proxy/app.vue'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ManifestsList from '~/packages_and_registries/dependency_proxy/components/manifests_list.vue'; @@ -21,13 +28,25 @@ import getDependencyProxyDetailsQuery from '~/packages_and_registries/dependency import { proxyDetailsQuery, proxyData, pagination, proxyManifests } from './mock_data'; +const dummyApiVersion = 'v3000'; +const dummyGrouptId = 1; +const dummyUrlRoot = '/gitlab'; +const dummyGon = { + api_version: dummyApiVersion, + relative_url_root: dummyUrlRoot, +}; +let originalGon; +const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/groups/${dummyGrouptId}/dependency_proxy/cache`; + describe('DependencyProxyApp', () => { let wrapper; let apolloProvider; let resolver; + let mock; const provideDefaults = { groupPath: 'gitlab-org', + groupId: dummyGrouptId, dependencyProxyAvailable: true, noManifestsIllustration: 'noManifestsIllustration', }; @@ -43,9 +62,14 @@ describe('DependencyProxyApp', () => { apolloProvider, provide, stubs: { + GlAlert, + GlDropdown, + GlDropdownItem, GlFormInputGroup, GlFormGroup, + GlModal, GlSprintf, + TitleArea, }, }); } @@ -59,13 +83,24 @@ describe('DependencyProxyApp', () => { const findProxyCountText = () => wrapper.findByTestId('proxy-count'); const findManifestList = () => wrapper.findComponent(ManifestsList); const findEmptyState = () => wrapper.findComponent(GlEmptyState); + const findClearCacheDropdownList = () => wrapper.findComponent(GlDropdown); + const findClearCacheModal = () => wrapper.findComponent(GlModal); + const findClearCacheAlert = () => wrapper.findComponent(GlAlert); beforeEach(() => { resolver = jest.fn().mockResolvedValue(proxyDetailsQuery()); + + originalGon = window.gon; + window.gon = { ...dummyGon }; + + mock = new MockAdapter(axios); + mock.onDelete(expectedUrl).reply(202, {}); }); afterEach(() => { wrapper.destroy(); + window.gon = originalGon; + mock.restore(); }); describe('when the dependency proxy is not available', () => { @@ -95,6 +130,12 @@ describe('DependencyProxyApp', () => { expect(resolver).not.toHaveBeenCalled(); }); + + it('hides the clear cache dropdown list', () => { + createComponent(createComponentArguments); + + expect(findClearCacheDropdownList().exists()).toBe(false); + }); }); describe('when the dependency proxy is available', () => { @@ -165,6 +206,7 @@ describe('DependencyProxyApp', () => { }), ); createComponent(); + return waitForPromises(); }); @@ -214,6 +256,34 @@ describe('DependencyProxyApp', () => { fullPath: provideDefaults.groupPath, }); }); + + it('shows the clear cache dropdown list', () => { + expect(findClearCacheDropdownList().exists()).toBe(true); + + const clearCacheDropdownItem = findClearCacheDropdownList().findComponent( + GlDropdownItem, + ); + + expect(clearCacheDropdownItem.text()).toBe('Clear cache'); + }); + + it('shows the clear cache confirmation modal', () => { + const modal = findClearCacheModal(); + + expect(modal.find('.modal-title').text()).toContain('Clear 2 images from cache?'); + expect(modal.props('actionPrimary').text).toBe('Clear cache'); + }); + + it('submits the clear cache request', async () => { + findClearCacheModal().vm.$emit('primary', { preventDefault: jest.fn() }); + + await waitForPromises(); + + expect(findClearCacheAlert().exists()).toBe(true); + expect(findClearCacheAlert().text()).toBe( + 'All items in the cache are scheduled for removal.', + ); + }); }); }); }); diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js new file mode 100644 index 00000000000..636f3eeb04a --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js @@ -0,0 +1,88 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlSprintf } from '@gitlab/ui'; +import { nextTick } from 'vue'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; +import HarborListHeader from '~/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue'; +import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; +import { + HARBOR_REGISTRY_TITLE, + LIST_INTRO_TEXT, +} from '~/packages_and_registries/harbor_registry/constants/index'; + +describe('harbor_list_header', () => { + let wrapper; + + const findTitleArea = () => wrapper.find(TitleArea); + const findCommandsSlot = () => wrapper.find('[data-testid="commands-slot"]'); + const findImagesMetaDataItem = () => wrapper.find(MetadataItem); + + const mountComponent = async (propsData, slots) => { + wrapper = shallowMount(HarborListHeader, { + stubs: { + GlSprintf, + TitleArea, + }, + propsData, + slots, + }); + await nextTick(); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('header', () => { + it('has a title', () => { + mountComponent({ metadataLoading: true }); + + expect(findTitleArea().props()).toMatchObject({ + title: HARBOR_REGISTRY_TITLE, + metadataLoading: true, + }); + }); + + it('has a commands slot', () => { + mountComponent(null, { commands: '<div data-testid="commands-slot">baz</div>' }); + + expect(findCommandsSlot().text()).toBe('baz'); + }); + + describe('sub header parts', () => { + describe('images count', () => { + it('exists', async () => { + await mountComponent({ imagesCount: 1 }); + + expect(findImagesMetaDataItem().exists()).toBe(true); + }); + + it('when there is one image', async () => { + await mountComponent({ imagesCount: 1 }); + + expect(findImagesMetaDataItem().props()).toMatchObject({ + text: '1 Image repository', + icon: 'container-image', + }); + }); + + it('when there is more than one image', async () => { + await mountComponent({ imagesCount: 3 }); + + expect(findImagesMetaDataItem().props('text')).toBe('3 Image repositories'); + }); + }); + }); + }); + + describe('info messages', () => { + describe('default message', () => { + it('is correctly bound to title_area props', () => { + mountComponent({ helpPagePath: 'foo' }); + + expect(findTitleArea().props('infoMessages')).toEqual([ + { text: LIST_INTRO_TEXT, link: 'foo' }, + ]); + }); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js new file mode 100644 index 00000000000..8560c4f78f7 --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js @@ -0,0 +1,99 @@ +import { shallowMount, RouterLinkStub as RouterLink } from '@vue/test-utils'; +import { GlIcon, GlSprintf, GlSkeletonLoader } from '@gitlab/ui'; + +import HarborListRow from '~/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue'; +import ListItem from '~/vue_shared/components/registry/list_item.vue'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { harborListResponse } from '../../mock_data'; + +describe('Harbor List Row', () => { + let wrapper; + const [item] = harborListResponse.repositories; + + const findDetailsLink = () => wrapper.find(RouterLink); + const findClipboardButton = () => wrapper.findComponent(ClipboardButton); + const findTagsCount = () => wrapper.find('[data-testid="tags-count"]'); + const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); + + const mountComponent = (props) => { + wrapper = shallowMount(HarborListRow, { + stubs: { + RouterLink, + GlSprintf, + ListItem, + }, + propsData: { + item, + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('image title and path', () => { + it('contains a link to the details page', () => { + mountComponent(); + + const link = findDetailsLink(); + expect(link.text()).toBe(item.name); + expect(findDetailsLink().props('to')).toMatchObject({ + name: 'details', + params: { + id: item.id, + }, + }); + }); + + it('contains a clipboard button', () => { + mountComponent(); + const button = findClipboardButton(); + expect(button.exists()).toBe(true); + expect(button.props('text')).toBe(item.location); + expect(button.props('title')).toBe(item.location); + }); + }); + + describe('tags count', () => { + it('exists', () => { + mountComponent(); + expect(findTagsCount().exists()).toBe(true); + }); + + it('contains a tag icon', () => { + mountComponent(); + const icon = findTagsCount().find(GlIcon); + expect(icon.exists()).toBe(true); + expect(icon.props('name')).toBe('tag'); + }); + + describe('loading state', () => { + it('shows a loader when metadataLoading is true', () => { + mountComponent({ metadataLoading: true }); + + expect(findSkeletonLoader().exists()).toBe(true); + }); + + it('hides the tags count while loading', () => { + mountComponent({ metadataLoading: true }); + + expect(findTagsCount().exists()).toBe(false); + }); + }); + + describe('tags count text', () => { + it('with one tag in the image', () => { + mountComponent({ item: { ...item, artifactCount: 1 } }); + + expect(findTagsCount().text()).toMatchInterpolatedText('1 Tag'); + }); + it('with more than one tag in the image', () => { + mountComponent({ item: { ...item, artifactCount: 3 } }); + + expect(findTagsCount().text()).toMatchInterpolatedText('3 Tags'); + }); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js new file mode 100644 index 00000000000..f018eff58c9 --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js @@ -0,0 +1,39 @@ +import { shallowMount } from '@vue/test-utils'; +import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue'; +import HarborListRow from '~/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue'; +import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; +import { harborListResponse } from '../../mock_data'; + +describe('Harbor List', () => { + let wrapper; + + const findHarborListRow = () => wrapper.findAll(HarborListRow); + + const mountComponent = (props) => { + wrapper = shallowMount(HarborList, { + stubs: { RegistryList }, + propsData: { + images: harborListResponse.repositories, + pageInfo: harborListResponse.pageInfo, + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('list', () => { + it('contains one list element for each image', () => { + mountComponent(); + + expect(findHarborListRow().length).toBe(harborListResponse.repositories.length); + }); + + it('passes down the metadataLoading prop', () => { + mountComponent({ metadataLoading: true }); + expect(findHarborListRow().at(0).props('metadataLoading')).toBe(true); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/harbor_registry/mock_data.js b/spec/frontend/packages_and_registries/harbor_registry/mock_data.js new file mode 100644 index 00000000000..85399c22e79 --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/mock_data.js @@ -0,0 +1,175 @@ +export const harborListResponse = { + repositories: [ + { + artifactCount: 1, + creationTime: '2022-03-02T06:35:53.205Z', + id: 25, + name: 'shao/flinkx', + projectId: 21, + pullCount: 0, + updateTime: '2022-03-02T06:35:53.205Z', + location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas', + }, + { + artifactCount: 1, + creationTime: '2022-03-02T06:35:53.205Z', + id: 26, + name: 'shao/flinkx1', + projectId: 21, + pullCount: 0, + updateTime: '2022-03-02T06:35:53.205Z', + location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas', + }, + { + artifactCount: 1, + creationTime: '2022-03-02T06:35:53.205Z', + id: 27, + name: 'shao/flinkx2', + projectId: 21, + pullCount: 0, + updateTime: '2022-03-02T06:35:53.205Z', + location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas', + }, + ], + totalCount: 3, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, +}; + +export const harborTagsResponse = { + tags: [ + { + digest: 'sha256:7f386a1844faf341353e1c20f2f39f11f397604fedc475435d13f756eeb235d1', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c', + name: '02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c', + revision: 'f53bde3d44699e04e11cf15fb415046a0913e2623d878d89bc21adb2cbda5255', + shortRevision: 'f53bde3d4', + createdAt: '2022-03-02T23:59:05+00:00', + totalSize: '6623124', + }, + { + digest: 'sha256:4554416b84c4568fe93086620b637064ed029737aabe7308b96d50e3d9d92ed7', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160', + name: '02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160', + revision: 'e1fe52d8bab66d71bd54a6b8784d3b9edbc68adbd6ea87f5fa44d9974144ef9e', + shortRevision: 'e1fe52d8b', + createdAt: '2022-02-10T01:09:56+00:00', + totalSize: '920760', + }, + { + digest: 'sha256:14f37b60e52b9ce0e9f8f7094b311d265384798592f783487c30aaa3d58e6345', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a', + name: '03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a', + revision: 'c72770c6eb93c421bc496964b4bffc742b1ec2e642cdab876be7afda1856029f', + shortRevision: 'c72770c6e', + createdAt: '2021-12-22T04:48:48+00:00', + totalSize: '48609053', + }, + { + digest: 'sha256:e925e3b8277ea23f387ed5fba5e78280cfac7cfb261a78cf046becf7b6a3faae', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19', + name: '03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19', + revision: '1ac2a43194f4e15166abdf3f26e6ec92215240490b9cac834d63de1a3d87494a', + shortRevision: '1ac2a4319', + createdAt: '2022-03-09T11:02:27+00:00', + totalSize: '35141894', + }, + { + digest: 'sha256:7d8303fd5c077787a8c879f8f66b69e2b5605f48ccd3f286e236fb0749fcc1ca', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda', + name: '05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda', + revision: 'cf8fee086701016e1a84e6824f0c896951fef4cce9d4745459558b87eec3232c', + shortRevision: 'cf8fee086', + createdAt: '2022-01-21T11:31:43+00:00', + totalSize: '48716070', + }, + { + digest: 'sha256:b33611cefe20e4a41a6e0dce356a5d7ef3c177ea7536a58652f5b3a4f2f83549', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a', + name: '093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a', + revision: '1a4b48198b13d55242c5164e64d41c4e9f75b5d9506bc6e0efc1534dd0dd1f15', + shortRevision: '1a4b48198', + createdAt: '2022-01-21T11:31:51+00:00', + totalSize: '6623127', + }, + { + digest: 'sha256:d25c3c020e2dbd4711a67b9fe308f4cbb7b0bb21815e722a02f91c570dc5d519', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7', + name: '09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7', + revision: '03e2e2777dde01c30469ee8c710973dd08a7a4f70494d7dc1583c24b525d7f61', + shortRevision: '03e2e2777', + createdAt: '2022-03-02T23:58:20+00:00', + totalSize: '911377', + }, + { + digest: 'sha256:fb760e4d2184e9e8e39d6917534d4610fe01009734698a5653b2de1391ba28f4', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95', + name: '09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95', + revision: '350e78d60646bf6967244448c6aaa14d21ecb9a0c6cf87e9ff0361cbe59b9012', + shortRevision: '350e78d60', + createdAt: '2022-01-19T13:49:14+00:00', + totalSize: '48710241', + }, + { + digest: 'sha256:407250f380cea92729cbc038c420e74900f53b852e11edc6404fe75a0fd2c402', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557', + name: '0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557', + revision: '76038370b7f3904364891457c4a6a234897255e6b9f45d0a852bf3a7e5257e18', + shortRevision: '76038370b', + createdAt: '2022-01-24T12:56:22+00:00', + totalSize: '280065', + }, + { + digest: 'sha256:ada87f25218542951ce6720c27f3d0758e90c2540bd129f5cfb9e15b31e07b07', + location: + 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb', + path: + 'gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb', + name: '0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb', + revision: '3d4b49a7bbb36c48bb721f4d0e76e7950bec3878ee29cdfdd6da39f575d6d37f', + shortRevision: '3d4b49a7b', + createdAt: '2022-02-17T17:37:52+00:00', + totalSize: '48655767', + }, + ], + totalCount: 100, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, +}; + +export const dockerCommands = { + dockerBuildCommand: 'foofoo', + dockerPushCommand: 'barbar', + dockerLoginCommand: 'bazbaz', +}; diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js new file mode 100644 index 00000000000..55fc8066f65 --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js @@ -0,0 +1,24 @@ +import { shallowMount } from '@vue/test-utils'; +import component from '~/packages_and_registries/harbor_registry/pages/index.vue'; + +describe('List Page', () => { + let wrapper; + + const findRouterView = () => wrapper.find({ ref: 'router-view' }); + + const mountComponent = () => { + wrapper = shallowMount(component, { + stubs: { + RouterView: true, + }, + }); + }; + + beforeEach(() => { + mountComponent(); + }); + + it('has a router view', () => { + expect(findRouterView().exists()).toBe(true); + }); +}); diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js new file mode 100644 index 00000000000..61ee36a2794 --- /dev/null +++ b/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js @@ -0,0 +1,140 @@ +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { GlSkeletonLoader } from '@gitlab/ui'; +import HarborListHeader from '~/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue'; +import HarborRegistryList from '~/packages_and_registries/harbor_registry/pages/list.vue'; +import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue'; +import waitForPromises from 'helpers/wait_for_promises'; +// import { harborListResponse } from '~/packages_and_registries/harbor_registry/mock_api.js'; +import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue'; +import CliCommands from '~/packages_and_registries/shared/components/cli_commands.vue'; +import { SORT_FIELDS } from '~/packages_and_registries/harbor_registry/constants/index'; +import { harborListResponse, dockerCommands } from '../mock_data'; + +let mockHarborListResponse; +jest.mock('~/packages_and_registries/harbor_registry/mock_api.js', () => ({ + harborListResponse: () => mockHarborListResponse, +})); + +describe('Harbor List Page', () => { + let wrapper; + + const waitForHarborPageRequest = async () => { + await waitForPromises(); + await nextTick(); + }; + + beforeEach(() => { + mockHarborListResponse = Promise.resolve(harborListResponse); + }); + + const findHarborListHeader = () => wrapper.findComponent(HarborListHeader); + const findPersistedSearch = () => wrapper.findComponent(PersistedSearch); + const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader); + const findHarborList = () => wrapper.findComponent(HarborList); + const findCliCommands = () => wrapper.findComponent(CliCommands); + + const fireFirstSortUpdate = () => { + findPersistedSearch().vm.$emit('update', { sort: 'UPDATED_DESC', filters: [] }); + }; + + const mountComponent = ({ config = { isGroupPage: false } } = {}) => { + wrapper = shallowMount(HarborRegistryList, { + stubs: { + HarborListHeader, + }, + provide() { + return { + config, + ...dockerCommands, + }; + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('contains harbor registry header', async () => { + mountComponent(); + fireFirstSortUpdate(); + await waitForHarborPageRequest(); + await nextTick(); + + expect(findHarborListHeader().exists()).toBe(true); + expect(findHarborListHeader().props()).toMatchObject({ + imagesCount: 3, + metadataLoading: false, + }); + }); + + describe('isLoading is true', () => { + it('shows the skeleton loader', async () => { + mountComponent(); + fireFirstSortUpdate(); + + expect(findSkeletonLoader().exists()).toBe(true); + }); + + it('harborList is not visible', () => { + mountComponent(); + + expect(findHarborList().exists()).toBe(false); + }); + + it('cli commands is not visible', () => { + mountComponent(); + + expect(findCliCommands().exists()).toBe(false); + }); + + it('title has the metadataLoading props set to true', async () => { + mountComponent(); + fireFirstSortUpdate(); + + expect(findHarborListHeader().props('metadataLoading')).toBe(true); + }); + }); + + describe('list is not empty', () => { + describe('unfiltered state', () => { + it('quick start is visible', async () => { + mountComponent(); + fireFirstSortUpdate(); + + await waitForHarborPageRequest(); + await nextTick(); + + expect(findCliCommands().exists()).toBe(true); + }); + + it('list component is visible', async () => { + mountComponent(); + fireFirstSortUpdate(); + + await waitForHarborPageRequest(); + await nextTick(); + + expect(findHarborList().exists()).toBe(true); + }); + }); + + describe('search and sorting', () => { + it('has a persisted search box element', async () => { + mountComponent(); + fireFirstSortUpdate(); + await waitForHarborPageRequest(); + await nextTick(); + + const harborRegistrySearch = findPersistedSearch(); + expect(harborRegistrySearch.exists()).toBe(true); + expect(harborRegistrySearch.props()).toMatchObject({ + defaultOrder: 'UPDATED', + defaultSort: 'desc', + sortableFields: SORT_FIELDS, + }); + }); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js index b9383d6c38c..31ab108558c 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/store/actions_spec.js @@ -20,10 +20,10 @@ jest.mock('~/api.js'); describe('Actions Package details store', () => { describe('fetchPackageVersions', () => { - it('should fetch the package versions', (done) => { + it('should fetch the package versions', async () => { Api.projectPackage = jest.fn().mockResolvedValue({ data: packageEntity }); - testAction( + await testAction( fetchPackageVersions, undefined, { packageEntity }, @@ -33,20 +33,14 @@ describe('Actions Package details store', () => { { type: types.SET_LOADING, payload: false }, ], [], - () => { - expect(Api.projectPackage).toHaveBeenCalledWith( - packageEntity.project_id, - packageEntity.id, - ); - done(); - }, ); + expect(Api.projectPackage).toHaveBeenCalledWith(packageEntity.project_id, packageEntity.id); }); - it("does not set the versions if they don't exist", (done) => { + it("does not set the versions if they don't exist", async () => { Api.projectPackage = jest.fn().mockResolvedValue({ data: { packageEntity, versions: null } }); - testAction( + await testAction( fetchPackageVersions, undefined, { packageEntity }, @@ -55,20 +49,14 @@ describe('Actions Package details store', () => { { type: types.SET_LOADING, payload: false }, ], [], - () => { - expect(Api.projectPackage).toHaveBeenCalledWith( - packageEntity.project_id, - packageEntity.id, - ); - done(); - }, ); + expect(Api.projectPackage).toHaveBeenCalledWith(packageEntity.project_id, packageEntity.id); }); - it('should create flash on API error', (done) => { + it('should create flash on API error', async () => { Api.projectPackage = jest.fn().mockRejectedValue(); - testAction( + await testAction( fetchPackageVersions, undefined, { packageEntity }, @@ -77,41 +65,31 @@ describe('Actions Package details store', () => { { type: types.SET_LOADING, payload: false }, ], [], - () => { - expect(Api.projectPackage).toHaveBeenCalledWith( - packageEntity.project_id, - packageEntity.id, - ); - expect(createFlash).toHaveBeenCalledWith({ - message: FETCH_PACKAGE_VERSIONS_ERROR, - type: 'warning', - }); - done(); - }, ); + expect(Api.projectPackage).toHaveBeenCalledWith(packageEntity.project_id, packageEntity.id); + expect(createFlash).toHaveBeenCalledWith({ + message: FETCH_PACKAGE_VERSIONS_ERROR, + type: 'warning', + }); }); }); describe('deletePackage', () => { - it('should call Api.deleteProjectPackage', (done) => { + it('should call Api.deleteProjectPackage', async () => { Api.deleteProjectPackage = jest.fn().mockResolvedValue(); - testAction(deletePackage, undefined, { packageEntity }, [], [], () => { - expect(Api.deleteProjectPackage).toHaveBeenCalledWith( - packageEntity.project_id, - packageEntity.id, - ); - done(); - }); + await testAction(deletePackage, undefined, { packageEntity }, [], []); + expect(Api.deleteProjectPackage).toHaveBeenCalledWith( + packageEntity.project_id, + packageEntity.id, + ); }); - it('should create flash on API error', (done) => { + it('should create flash on API error', async () => { Api.deleteProjectPackage = jest.fn().mockRejectedValue(); - testAction(deletePackage, undefined, { packageEntity }, [], [], () => { - expect(createFlash).toHaveBeenCalledWith({ - message: DELETE_PACKAGE_ERROR_MESSAGE, - type: 'warning', - }); - done(); + await testAction(deletePackage, undefined, { packageEntity }, [], []); + expect(createFlash).toHaveBeenCalledWith({ + message: DELETE_PACKAGE_ERROR_MESSAGE, + type: 'warning', }); }); }); @@ -119,37 +97,33 @@ describe('Actions Package details store', () => { describe('deletePackageFile', () => { const fileId = 'a_file_id'; - it('should call Api.deleteProjectPackageFile and commit the right data', (done) => { + it('should call Api.deleteProjectPackageFile and commit the right data', async () => { const packageFiles = [{ id: 'foo' }, { id: fileId }]; Api.deleteProjectPackageFile = jest.fn().mockResolvedValue(); - testAction( + await testAction( deletePackageFile, fileId, { packageEntity, packageFiles }, [{ type: types.UPDATE_PACKAGE_FILES, payload: [{ id: 'foo' }] }], [], - () => { - expect(Api.deleteProjectPackageFile).toHaveBeenCalledWith( - packageEntity.project_id, - packageEntity.id, - fileId, - ); - expect(createFlash).toHaveBeenCalledWith({ - message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE, - type: 'success', - }); - done(); - }, ); + expect(Api.deleteProjectPackageFile).toHaveBeenCalledWith( + packageEntity.project_id, + packageEntity.id, + fileId, + ); + expect(createFlash).toHaveBeenCalledWith({ + message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE, + type: 'success', + }); }); - it('should create flash on API error', (done) => { + + it('should create flash on API error', async () => { Api.deleteProjectPackageFile = jest.fn().mockRejectedValue(); - testAction(deletePackageFile, fileId, { packageEntity }, [], [], () => { - expect(createFlash).toHaveBeenCalledWith({ - message: DELETE_PACKAGE_FILE_ERROR_MESSAGE, - type: 'warning', - }); - done(); + await testAction(deletePackageFile, fileId, { packageEntity }, [], []); + expect(createFlash).toHaveBeenCalledWith({ + message: DELETE_PACKAGE_FILE_ERROR_MESSAGE, + type: 'warning', }); }); }); diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap index d82af8f9e63..a33528d2d91 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap @@ -21,7 +21,7 @@ exports[`packages_list_app renders 1`] = ` > <img alt="" - class="gl-max-w-full" + class="gl-max-w-full gl-dark-invert-keep-hue" role="img" src="helpSvg" /> diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js index 3fbfe1060dc..d596f2dae33 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js @@ -32,8 +32,8 @@ describe('Actions Package list store', () => { }; const filter = []; - it('should fetch the project packages list when isGroupPage is false', (done) => { - testAction( + it('should fetch the project packages list when isGroupPage is false', async () => { + await testAction( actions.requestPackagesList, undefined, { config: { isGroupPage: false, resourceId: 1 }, sorting, filter }, @@ -43,17 +43,14 @@ describe('Actions Package list store', () => { { type: 'receivePackagesListSuccess', payload: { data: 'foo', headers } }, { type: 'setLoading', payload: false }, ], - () => { - expect(Api.projectPackages).toHaveBeenCalledWith(1, { - params: { page: 1, per_page: 20, sort: sorting.sort, order_by: sorting.orderBy }, - }); - done(); - }, ); + expect(Api.projectPackages).toHaveBeenCalledWith(1, { + params: { page: 1, per_page: 20, sort: sorting.sort, order_by: sorting.orderBy }, + }); }); - it('should fetch the group packages list when isGroupPage is true', (done) => { - testAction( + it('should fetch the group packages list when isGroupPage is true', async () => { + await testAction( actions.requestPackagesList, undefined, { config: { isGroupPage: true, resourceId: 2 }, sorting, filter }, @@ -63,19 +60,16 @@ describe('Actions Package list store', () => { { type: 'receivePackagesListSuccess', payload: { data: 'baz', headers } }, { type: 'setLoading', payload: false }, ], - () => { - expect(Api.groupPackages).toHaveBeenCalledWith(2, { - params: { page: 1, per_page: 20, sort: sorting.sort, order_by: sorting.orderBy }, - }); - done(); - }, ); + expect(Api.groupPackages).toHaveBeenCalledWith(2, { + params: { page: 1, per_page: 20, sort: sorting.sort, order_by: sorting.orderBy }, + }); }); - it('should fetch packages of a certain type when a filter with a type is present', (done) => { + it('should fetch packages of a certain type when a filter with a type is present', async () => { const packageType = 'maven'; - testAction( + await testAction( actions.requestPackagesList, undefined, { @@ -89,24 +83,21 @@ describe('Actions Package list store', () => { { type: 'receivePackagesListSuccess', payload: { data: 'foo', headers } }, { type: 'setLoading', payload: false }, ], - () => { - expect(Api.projectPackages).toHaveBeenCalledWith(1, { - params: { - page: 1, - per_page: 20, - sort: sorting.sort, - order_by: sorting.orderBy, - package_type: packageType, - }, - }); - done(); - }, ); + expect(Api.projectPackages).toHaveBeenCalledWith(1, { + params: { + page: 1, + per_page: 20, + sort: sorting.sort, + order_by: sorting.orderBy, + package_type: packageType, + }, + }); }); - it('should create flash on API error', (done) => { + it('should create flash on API error', async () => { Api.projectPackages = jest.fn().mockRejectedValue(); - testAction( + await testAction( actions.requestPackagesList, undefined, { config: { isGroupPage: false, resourceId: 2 }, sorting, filter }, @@ -115,15 +106,12 @@ describe('Actions Package list store', () => { { type: 'setLoading', payload: true }, { type: 'setLoading', payload: false }, ], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, ); + expect(createFlash).toHaveBeenCalled(); }); - it('should force the terraform_module type when forceTerraform is true', (done) => { - testAction( + it('should force the terraform_module type when forceTerraform is true', async () => { + await testAction( actions.requestPackagesList, undefined, { config: { isGroupPage: false, resourceId: 1, forceTerraform: true }, sorting, filter }, @@ -133,27 +121,24 @@ describe('Actions Package list store', () => { { type: 'receivePackagesListSuccess', payload: { data: 'foo', headers } }, { type: 'setLoading', payload: false }, ], - () => { - expect(Api.projectPackages).toHaveBeenCalledWith(1, { - params: { - page: 1, - per_page: 20, - sort: sorting.sort, - order_by: sorting.orderBy, - package_type: 'terraform_module', - }, - }); - done(); - }, ); + expect(Api.projectPackages).toHaveBeenCalledWith(1, { + params: { + page: 1, + per_page: 20, + sort: sorting.sort, + order_by: sorting.orderBy, + package_type: 'terraform_module', + }, + }); }); }); describe('receivePackagesListSuccess', () => { - it('should set received packages', (done) => { + it('should set received packages', () => { const data = 'foo'; - testAction( + return testAction( actions.receivePackagesListSuccess, { data, headers }, null, @@ -162,33 +147,30 @@ describe('Actions Package list store', () => { { type: types.SET_PAGINATION, payload: headers }, ], [], - done, ); }); }); describe('setInitialState', () => { - it('should commit setInitialState', (done) => { - testAction( + it('should commit setInitialState', () => { + return testAction( actions.setInitialState, '1', null, [{ type: types.SET_INITIAL_STATE, payload: '1' }], [], - done, ); }); }); describe('setLoading', () => { - it('should commit set main loading', (done) => { - testAction( + it('should commit set main loading', () => { + return testAction( actions.setLoading, true, null, [{ type: types.SET_MAIN_LOADING, payload: true }], [], - done, ); }); }); @@ -199,11 +181,11 @@ describe('Actions Package list store', () => { delete_api_path: 'foo', }, }; - it('should perform a delete operation on _links.delete_api_path', (done) => { + it('should perform a delete operation on _links.delete_api_path', () => { mock.onDelete(payload._links.delete_api_path).replyOnce(200); Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' }); - testAction( + return testAction( actions.requestDeletePackage, payload, { pagination: { page: 1 } }, @@ -212,13 +194,12 @@ describe('Actions Package list store', () => { { type: 'setLoading', payload: true }, { type: 'requestPackagesList', payload: { page: 1 } }, ], - done, ); }); - it('should stop the loading and call create flash on api error', (done) => { + it('should stop the loading and call create flash on api error', async () => { mock.onDelete(payload._links.delete_api_path).replyOnce(400); - testAction( + await testAction( actions.requestDeletePackage, payload, null, @@ -227,50 +208,44 @@ describe('Actions Package list store', () => { { type: 'setLoading', payload: true }, { type: 'setLoading', payload: false }, ], - () => { - expect(createFlash).toHaveBeenCalled(); - done(); - }, ); + expect(createFlash).toHaveBeenCalled(); }); it.each` property | actionPayload ${'_links'} | ${{}} ${'delete_api_path'} | ${{ _links: {} }} - `('should reject and createFlash when $property is missing', ({ actionPayload }, done) => { - testAction(actions.requestDeletePackage, actionPayload, null, [], []).catch((e) => { + `('should reject and createFlash when $property is missing', ({ actionPayload }) => { + return testAction(actions.requestDeletePackage, actionPayload, null, [], []).catch((e) => { expect(e).toEqual(new Error(MISSING_DELETE_PATH_ERROR)); expect(createFlash).toHaveBeenCalledWith({ message: DELETE_PACKAGE_ERROR_MESSAGE, }); - done(); }); }); }); describe('setSorting', () => { - it('should commit SET_SORTING', (done) => { - testAction( + it('should commit SET_SORTING', () => { + return testAction( actions.setSorting, 'foo', null, [{ type: types.SET_SORTING, payload: 'foo' }], [], - done, ); }); }); describe('setFilter', () => { - it('should commit SET_FILTER', (done) => { - testAction( + it('should commit SET_FILTER', () => { + return testAction( actions.setFilter, 'foo', null, [{ type: types.SET_FILTER, payload: 'foo' }], [], - done, ); }); }); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js index 9e91b15bc6e..3670cfca8ea 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js @@ -73,7 +73,6 @@ describe('Package Search', () => { mountComponent(); expect(findLocalStorageSync().props()).toMatchObject({ - asJson: true, storageKey: 'package_registry_list_sorting', value: { orderBy: LIST_KEY_CREATED_AT, diff --git a/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap index 0154486e224..17905a8db2d 100644 --- a/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap +++ b/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap @@ -21,7 +21,7 @@ exports[`PackagesListApp renders 1`] = ` > <img alt="" - class="gl-max-w-full" + class="gl-max-w-full gl-dark-invert-keep-hue" role="img" src="emptyListIllustration" /> diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js index a6c929844b1..0a72f0269ee 100644 --- a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js +++ b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js @@ -12,7 +12,6 @@ import { UNAVAILABLE_USER_FEATURE_TEXT, } from '~/packages_and_registries/settings/project/constants'; import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql'; -import CleanupPolicyEnabledAlert from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue'; import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; import { @@ -31,12 +30,11 @@ describe('Registry Settings App', () => { adminSettingsPath: 'settingsPath', enableHistoricEntries: false, helpPagePath: 'helpPagePath', - showCleanupPolicyOnAlert: false, + showCleanupPolicyLink: false, }; const findSettingsComponent = () => wrapper.find(SettingsForm); const findAlert = () => wrapper.find(GlAlert); - const findCleanupAlert = () => wrapper.findComponent(CleanupPolicyEnabledAlert); const mountComponent = (provide = defaultProvidedValues, config) => { wrapper = shallowMount(component, { @@ -69,26 +67,6 @@ describe('Registry Settings App', () => { wrapper.destroy(); }); - describe('cleanup is on alert', () => { - it('exist when showCleanupPolicyOnAlert is true and has the correct props', () => { - mountComponent({ - ...defaultProvidedValues, - showCleanupPolicyOnAlert: true, - }); - - expect(findCleanupAlert().exists()).toBe(true); - expect(findCleanupAlert().props()).toMatchObject({ - projectPath: 'path', - }); - }); - - it('is hidden when showCleanupPolicyOnAlert is false', async () => { - mountComponent(); - - expect(findCleanupAlert().exists()).toBe(false); - }); - }); - describe('isEdited status', () => { it.each` description | apiResponse | workingCopy | result diff --git a/spec/frontend/packages_and_registries/shared/components/__snapshots__/cleanup_policy_enabled_alert_spec.js.snap b/spec/frontend/packages_and_registries/shared/components/__snapshots__/cleanup_policy_enabled_alert_spec.js.snap deleted file mode 100644 index 2cded2ead2e..00000000000 --- a/spec/frontend/packages_and_registries/shared/components/__snapshots__/cleanup_policy_enabled_alert_spec.js.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CleanupPolicyEnabledAlert renders 1`] = ` -<gl-alert-stub - class="gl-mt-2" - dismissible="true" - dismisslabel="Dismiss" - primarybuttonlink="" - primarybuttontext="" - secondarybuttonlink="" - secondarybuttontext="" - title="" - variant="info" -> - <gl-sprintf-stub - message="Cleanup policies are now available for this project. %{linkStart}Click here to get started.%{linkEnd}" - /> -</gl-alert-stub> -`; diff --git a/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap b/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap index ceae8eebaef..3dd6023140f 100644 --- a/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap +++ b/spec/frontend/packages_and_registries/shared/components/__snapshots__/registry_breadcrumb_spec.js.snap @@ -10,11 +10,10 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = ` class="breadcrumb gl-breadcrumb-list" > <li - class="breadcrumb-item gl-breadcrumb-item" + class="gl-breadcrumb-item" > <a class="" - href="/" target="_self" > <span> @@ -45,9 +44,10 @@ exports[`Registry Breadcrumb when is not rootRoute renders 1`] = ` <!----> <li - class="breadcrumb-item gl-breadcrumb-item" + class="gl-breadcrumb-item" > <a + aria-current="page" class="" href="#" target="_self" @@ -75,11 +75,11 @@ exports[`Registry Breadcrumb when is rootRoute renders 1`] = ` class="breadcrumb gl-breadcrumb-list" > <li - class="breadcrumb-item gl-breadcrumb-item" + class="gl-breadcrumb-item" > <a + aria-current="page" class="" - href="/" target="_self" > <span> diff --git a/spec/frontend/packages_and_registries/shared/components/cleanup_policy_enabled_alert_spec.js b/spec/frontend/packages_and_registries/shared/components/cleanup_policy_enabled_alert_spec.js deleted file mode 100644 index 269e087f5ac..00000000000 --- a/spec/frontend/packages_and_registries/shared/components/cleanup_policy_enabled_alert_spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import { GlAlert } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import component from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue'; -import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; - -describe('CleanupPolicyEnabledAlert', () => { - let wrapper; - - const defaultProps = { - projectPath: 'foo', - cleanupPoliciesSettingsPath: 'label-bar', - }; - - const findAlert = () => wrapper.findComponent(GlAlert); - - const mountComponent = (props) => { - wrapper = shallowMount(component, { - stubs: { - LocalStorageSync, - }, - propsData: { - ...defaultProps, - ...props, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - it('renders', () => { - mountComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('when dismissed is not visible', async () => { - mountComponent(); - - expect(findAlert().exists()).toBe(true); - findAlert().vm.$emit('dismiss'); - - await nextTick(); - - expect(findAlert().exists()).toBe(false); - }); -}); diff --git a/spec/frontend/packages_and_registries/shared/components/registry_breadcrumb_spec.js b/spec/frontend/packages_and_registries/shared/components/registry_breadcrumb_spec.js index 6dfe116c285..15db454ac68 100644 --- a/spec/frontend/packages_and_registries/shared/components/registry_breadcrumb_spec.js +++ b/spec/frontend/packages_and_registries/shared/components/registry_breadcrumb_spec.js @@ -1,4 +1,4 @@ -import { mount } from '@vue/test-utils'; +import { mount, RouterLinkStub } from '@vue/test-utils'; import component from '~/packages_and_registries/shared/components/registry_breadcrumb.vue'; @@ -21,6 +21,9 @@ describe('Registry Breadcrumb', () => { }, }, }, + stubs: { + RouterLink: RouterLinkStub, + }, }); }; @@ -30,7 +33,6 @@ describe('Registry Breadcrumb', () => { afterEach(() => { wrapper.destroy(); - wrapper = null; }); describe('when is rootRoute', () => { @@ -46,7 +48,6 @@ describe('Registry Breadcrumb', () => { const links = wrapper.findAll('a'); expect(links).toHaveLength(1); - expect(links.at(0).attributes('href')).toBe('/'); }); it('the link text is calculated by nameGenerator', () => { @@ -67,7 +68,6 @@ describe('Registry Breadcrumb', () => { const links = wrapper.findAll('a'); expect(links).toHaveLength(2); - expect(links.at(0).attributes('href')).toBe('/'); expect(links.at(1).attributes('href')).toBe('#'); }); |