summaryrefslogtreecommitdiff
path: root/spec/frontend/packages_and_registries
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 13:49:51 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 13:49:51 +0000
commit71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch)
tree6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /spec/frontend/packages_and_registries
parenta7253423e3403b8c08f8a161e5937e1488f5f407 (diff)
downloadgitlab-ce-71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e.tar.gz
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'spec/frontend/packages_and_registries')
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/app_spec.js3
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap1
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js5
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap14
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap104
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/package_versions_list_spec.js90
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/version_row_spec.js109
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/functional/delete_packages_spec.js (renamed from spec/frontend/packages_and_registries/package_registry/components/functional/delete_package_spec.js)78
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap25
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js43
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/mock_data.js44
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/details_spec.js56
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/list_spec.js75
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js64
-rw-r--r--spec/frontend/packages_and_registries/shared/components/registry_list_spec.js28
16 files changed, 448 insertions, 295 deletions
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 329cc15df97..601f8abd34d 100644
--- a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
+++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
@@ -17,6 +17,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
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 { HTTP_STATUS_ACCEPTED } from '~/lib/utils/http_status';
import DependencyProxyApp from '~/packages_and_registries/dependency_proxy/app.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
@@ -92,7 +93,7 @@ describe('DependencyProxyApp', () => {
window.gon = { ...dummyGon };
mock = new MockAdapter(axios);
- mock.onDelete(expectedUrl).reply(202, {});
+ mock.onDelete(expectedUrl).reply(HTTP_STATUS_ACCEPTED, {});
});
afterEach(() => {
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 a33528d2d91..801cde8582e 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
@@ -30,6 +30,7 @@ exports[`packages_list_app renders 1`] = `
<div
class="gl-max-w-full gl-m-auto"
+ data-testid="gl-empty-state-content"
>
<div
class="gl-mx-auto gl-my-0 gl-p-5"
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 36417eaf793..2c185e040f4 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
@@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
import { createAlert } from '~/flash';
+import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { MISSING_DELETE_PATH_ERROR } from '~/packages_and_registries/infrastructure_registry/list/constants';
import * as actions from '~/packages_and_registries/infrastructure_registry/list/stores/actions';
import * as types from '~/packages_and_registries/infrastructure_registry/list/stores/mutation_types';
@@ -182,7 +183,7 @@ describe('Actions Package list store', () => {
},
};
it('should perform a delete operation on _links.delete_api_path', () => {
- mock.onDelete(payload._links.delete_api_path).replyOnce(200);
+ mock.onDelete(payload._links.delete_api_path).replyOnce(HTTP_STATUS_OK);
Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' });
return testAction(
@@ -198,7 +199,7 @@ describe('Actions Package list store', () => {
});
it('should stop the loading and call create flash on api error', async () => {
- mock.onDelete(payload._links.delete_api_path).replyOnce(400);
+ mock.onDelete(payload._links.delete_api_path).replyOnce(HTTP_STATUS_BAD_REQUEST);
await testAction(
actions.requestDeletePackage,
payload,
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
index 91824dee5b0..08e2de6c18f 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
@@ -101,18 +101,6 @@ exports[`packages_list_row renders 1`] = `
</div>
</div>
- <div
- class="gl-display-flex"
- >
- <div
- class="gl-w-7"
- />
-
- <!---->
-
- <div
- class="gl-w-9"
- />
- </div>
+ <!---->
</div>
`;
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap
deleted file mode 100644
index bdd0fe3ad9e..00000000000
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/version_row_spec.js.snap
+++ /dev/null
@@ -1,104 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`VersionRow renders 1`] = `
-<div
- class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
->
- <div
- class="gl-display-flex gl-align-items-center gl-py-3"
- >
- <!---->
-
- <div
- class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-grow-1"
- >
- <div
- class="gl-display-flex gl-flex-direction-column gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1"
- >
- <div
- class="gl-display-flex gl-align-items-center gl-text-body gl-font-weight-bold gl-min-h-6 gl-min-w-0"
- >
- <div
- class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0"
- >
- <gl-link-stub
- class="gl-text-body gl-min-w-0"
- href="243"
- >
- <span
- class="gl-truncate"
- data-testid="truncate-end-container"
- title="@gitlab-org/package-15"
- >
- <span
- class="gl-truncate-end"
- >
- @gitlab-org/package-15
- </span>
- </span>
- </gl-link-stub>
-
- <package-tags-stub
- class="gl-ml-3"
- hidelabel="true"
- tagdisplaylimit="1"
- tags="[object Object],[object Object],[object Object]"
- />
- </div>
-
- <!---->
- </div>
-
- <div
- class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-grow-1"
- >
-
- 1.0.1
-
- </div>
- </div>
-
- <div
- class="gl-display-flex gl-flex-direction-column gl-sm-align-items-flex-end gl-justify-content-space-between gl-text-gray-500 gl-flex-shrink-0"
- >
- <div
- class="gl-display-flex gl-align-items-center gl-sm-text-body gl-sm-font-weight-bold gl-min-h-6"
- >
- <publish-method-stub
- packageentity="[object Object]"
- />
- </div>
-
- <div
- class="gl-display-flex gl-align-items-center gl-min-h-6"
- >
- <span>
- Created
- <time-ago-tooltip-stub
- cssclass=""
- time="2021-08-10T09:33:54Z"
- tooltipplacement="top"
- />
- </span>
- </div>
- </div>
- </div>
-
- <!---->
- </div>
-
- <div
- class="gl-display-flex"
- >
- <div
- class="gl-w-7"
- />
-
- <!---->
-
- <div
- class="gl-w-9"
- />
- </div>
-</div>
-`;
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 20a459e2c1a..27c0ab96cfc 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,8 +1,16 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue';
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
import VersionRow from '~/packages_and_registries/package_registry/components/details/version_row.vue';
+import Tracking from '~/tracking';
+import {
+ CANCEL_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+ DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+ REQUEST_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+} from '~/packages_and_registries/package_registry/constants';
import { packageData } from '../../mock_data';
describe('PackageVersionsList', () => {
@@ -24,6 +32,7 @@ describe('PackageVersionsList', () => {
findRegistryList: () => wrapper.findComponent(RegistryList),
findEmptySlot: () => wrapper.findComponent(EmptySlotStub),
findListRow: () => wrapper.findAllComponents(VersionRow),
+ findDeletePackagesModal: () => wrapper.findComponent(DeleteModal),
};
const mountComponent = (props) => {
wrapper = shallowMountExtended(PackageVersionsList, {
@@ -35,6 +44,11 @@ describe('PackageVersionsList', () => {
},
stubs: {
RegistryList,
+ DeleteModal: stubComponent(DeleteModal, {
+ methods: {
+ show: jest.fn(),
+ },
+ }),
},
slots: {
'empty-state': EmptySlotStub,
@@ -144,4 +158,80 @@ describe('PackageVersionsList', () => {
expect(wrapper.emitted('next-page')).toHaveLength(1);
});
});
+
+ describe('when the user can bulk destroy versions', () => {
+ let eventSpy;
+ const { findDeletePackagesModal, findRegistryList } = uiElements;
+
+ beforeEach(() => {
+ eventSpy = jest.spyOn(Tracking, 'event');
+ mountComponent({ canDestroy: true });
+ });
+
+ it('binds the right props', () => {
+ expect(uiElements.findRegistryList().props()).toMatchObject({
+ items: packageList,
+ pagination: {},
+ isLoading: false,
+ hiddenDelete: false,
+ title: '2 versions',
+ });
+ });
+
+ describe('upon deletion', () => {
+ beforeEach(() => {
+ findRegistryList().vm.$emit('delete', packageList);
+ });
+
+ it('passes itemsToBeDeleted to the modal', () => {
+ expect(findDeletePackagesModal().props('itemsToBeDeleted')).toStrictEqual(packageList);
+ expect(wrapper.emitted('delete')).toBeUndefined();
+ });
+
+ it('requesting delete tracks the right action', () => {
+ expect(eventSpy).toHaveBeenCalledWith(
+ undefined,
+ REQUEST_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+ expect.any(Object),
+ );
+ });
+
+ describe('when modal confirms', () => {
+ beforeEach(() => {
+ findDeletePackagesModal().vm.$emit('confirm');
+ });
+
+ it('emits delete event', () => {
+ expect(wrapper.emitted('delete')[0]).toEqual([packageList]);
+ });
+
+ it('tracks the right action', () => {
+ expect(eventSpy).toHaveBeenCalledWith(
+ undefined,
+ DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+ expect.any(Object),
+ );
+ });
+ });
+
+ it.each(['confirm', 'cancel'])(
+ 'resets itemsToBeDeleted when modal emits %s',
+ async (event) => {
+ await findDeletePackagesModal().vm.$emit(event);
+
+ expect(findDeletePackagesModal().props('itemsToBeDeleted')).toHaveLength(0);
+ },
+ );
+
+ it('canceling delete tracks the right action', () => {
+ findDeletePackagesModal().vm.$emit('cancel');
+
+ expect(eventSpy).toHaveBeenCalledWith(
+ undefined,
+ CANCEL_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
+ expect.any(Object),
+ );
+ });
+ });
+ });
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/version_row_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/version_row_spec.js
index faeca76d746..67340822fa5 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/version_row_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/version_row_spec.js
@@ -1,11 +1,13 @@
-import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
+import { GlFormCheckbox, GlIcon, GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import ListItem from '~/vue_shared/components/registry/list_item.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
import PublishMethod from '~/packages_and_registries/shared/components/publish_method.vue';
import VersionRow from '~/packages_and_registries/package_registry/components/details/version_row.vue';
-import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { PACKAGE_ERROR_STATUS } from '~/packages_and_registries/package_registry/constants';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { packageVersions } from '../../mock_data';
@@ -19,17 +21,23 @@ describe('VersionRow', () => {
const findPackageTags = () => wrapper.findComponent(PackageTags);
const findPublishMethod = () => wrapper.findComponent(PublishMethod);
const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
+ const findPackageName = () => wrapper.findComponent(GlTruncate);
+ const findWarningIcon = () => wrapper.findComponent(GlIcon);
+ const findBulkDeleteAction = () => wrapper.findComponent(GlFormCheckbox);
- function createComponent(packageEntity = packageVersion) {
+ function createComponent({ packageEntity = packageVersion, selected = false } = {}) {
wrapper = shallowMountExtended(VersionRow, {
propsData: {
packageEntity,
+ selected,
},
stubs: {
- ListItem,
GlSprintf,
GlTruncate,
},
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
});
}
@@ -37,16 +45,15 @@ describe('VersionRow', () => {
wrapper.destroy();
});
- it('renders', () => {
+ it('has a link to the version detail', () => {
createComponent();
- expect(wrapper.element).toMatchSnapshot();
+ expect(findLink().attributes('href')).toBe(`${getIdFromGraphQLId(packageVersion.id)}`);
});
- it('has a link to the version detail', () => {
+ it('lists the package name', () => {
createComponent();
- expect(findLink().attributes('href')).toBe(`${getIdFromGraphQLId(packageVersion.id)}`);
expect(findLink().text()).toBe(packageVersion.name);
});
@@ -73,17 +80,89 @@ describe('VersionRow', () => {
expect(findTimeAgoTooltip().props('time')).toBe(packageVersion.createdAt);
});
- describe('disabled status', () => {
- it('disables the list item', () => {
- createComponent({ ...packageVersion, status: 'something' });
+ describe('left action template', () => {
+ it('does not render checkbox if not permitted', () => {
+ createComponent({ packageEntity: { ...packageVersion, canDestroy: false } });
+
+ expect(findBulkDeleteAction().exists()).toBe(false);
+ });
+
+ it('renders checkbox', () => {
+ createComponent();
+
+ expect(findBulkDeleteAction().exists()).toBe(true);
+ expect(findBulkDeleteAction().attributes('checked')).toBeUndefined();
+ });
+
+ it('emits select when checked', () => {
+ createComponent();
+
+ findBulkDeleteAction().vm.$emit('change');
+
+ expect(wrapper.emitted('select')).toHaveLength(1);
+ });
+
+ it('renders checkbox in selected state if selected', () => {
+ createComponent({
+ selected: true,
+ });
+
+ expect(findBulkDeleteAction().attributes('checked')).toBe('true');
+ expect(findListItem().props('selected')).toBe(true);
+ });
+ });
+
+ describe(`when the package is in ${PACKAGE_ERROR_STATUS} status`, () => {
+ beforeEach(() => {
+ createComponent({
+ packageEntity: {
+ ...packageVersion,
+ status: PACKAGE_ERROR_STATUS,
+ _links: {
+ webPath: null,
+ },
+ },
+ });
+ });
+
+ it('lists the package name', () => {
+ expect(findPackageName().props('text')).toBe('@gitlab-org/package-15');
+ });
+
+ it('does not have a link to navigate to the details page', () => {
+ expect(findLink().exists()).toBe(false);
+ });
+
+ it('has a warning icon', () => {
+ const icon = findWarningIcon();
+ const tooltip = getBinding(icon.element, 'gl-tooltip');
+ expect(icon.props('name')).toBe('warning');
+ expect(icon.props('ariaLabel')).toBe('Warning');
+ expect(tooltip.value).toMatchObject({
+ title: 'Invalid Package: failed metadata extraction',
+ });
+ });
+ });
- expect(findListItem().props('disabled')).toBe(true);
+ describe('disabled status', () => {
+ beforeEach(() => {
+ createComponent({
+ packageEntity: {
+ ...packageVersion,
+ status: 'something',
+ _links: {
+ webPath: null,
+ },
+ },
+ });
});
- it('disables the link', () => {
- createComponent({ ...packageVersion, status: 'something' });
+ it('lists the package name', () => {
+ expect(findPackageName().props('text')).toBe('@gitlab-org/package-15');
+ });
- expect(findLink().attributes('disabled')).toBe('true');
+ it('does not have a link to navigate to the details page', () => {
+ expect(findLink().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/functional/delete_package_spec.js b/spec/frontend/packages_and_registries/package_registry/components/functional/delete_packages_spec.js
index 93c2196b210..689b53fa2a4 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/functional/delete_package_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/functional/delete_packages_spec.js
@@ -4,36 +4,38 @@ import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/flash';
-import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
+import DeletePackages from '~/packages_and_registries/package_registry/components/functional/delete_packages.vue';
-import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
+import destroyPackagesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql';
import getPackagesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql';
import {
- packageDestroyMutation,
- packageDestroyMutationError,
+ packagesDestroyMutation,
+ packagesDestroyMutationError,
packagesListQuery,
} from '../../mock_data';
jest.mock('~/flash');
-describe('DeletePackage', () => {
+describe('DeletePackages', () => {
let wrapper;
let apolloProvider;
let resolver;
let mutationResolver;
- const eventPayload = { id: '1' };
+ const eventPayload = [{ id: '1' }];
+ const eventPayloadMultiple = [{ id: '1' }, { id: '2' }];
+ const mutationPayload = { ids: ['1'] };
function createComponent(propsData = {}) {
Vue.use(VueApollo);
const requestHandlers = [
[getPackagesQuery, resolver],
- [destroyPackageMutation, mutationResolver],
+ [destroyPackagesMutation, mutationResolver],
];
apolloProvider = createMockApollo(requestHandlers);
- wrapper = shallowMountExtended(DeletePackage, {
+ wrapper = shallowMountExtended(DeletePackages, {
propsData,
apolloProvider,
scopedSlots: {
@@ -43,7 +45,9 @@ describe('DeletePackage', () => {
'data-testid': 'trigger-button',
},
on: {
- click: props.deletePackage,
+ click: (payload) => {
+ return props.deletePackages(payload[0]);
+ },
},
});
},
@@ -54,23 +58,23 @@ describe('DeletePackage', () => {
const findButton = () => wrapper.findByTestId('trigger-button');
const clickOnButtonAndWait = (payload) => {
- findButton().trigger('click', payload);
+ findButton().trigger('click', [payload]);
return waitForPromises();
};
beforeEach(() => {
resolver = jest.fn().mockResolvedValue(packagesListQuery());
- mutationResolver = jest.fn().mockResolvedValue(packageDestroyMutation());
+ mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutation());
});
afterEach(() => {
wrapper.destroy();
});
- it('binds deletePackage method to the default slot', () => {
+ it('binds deletePackages method to the default slot', () => {
createComponent();
- findButton().trigger('click');
+ findButton().trigger('click', eventPayload);
expect(wrapper.emitted('start')).toEqual([[]]);
});
@@ -80,7 +84,7 @@ describe('DeletePackage', () => {
await clickOnButtonAndWait(eventPayload);
- expect(mutationResolver).toHaveBeenCalledWith(eventPayload);
+ expect(mutationResolver).toHaveBeenCalledWith(mutationPayload);
});
it('passes refetchQueries to apollo mutate', async () => {
@@ -91,10 +95,20 @@ describe('DeletePackage', () => {
await clickOnButtonAndWait(eventPayload);
- expect(mutationResolver).toHaveBeenCalledWith(eventPayload);
+ expect(mutationResolver).toHaveBeenCalledWith(mutationPayload);
expect(resolver).toHaveBeenCalledWith(variables);
});
+ describe('when payload contains multiple packages', () => {
+ it('calls apollo mutation with different payload', async () => {
+ createComponent();
+
+ await clickOnButtonAndWait(eventPayloadMultiple);
+
+ expect(mutationResolver).toHaveBeenCalledWith({ ids: ['1', '2'] });
+ });
+ });
+
describe('on mutation success', () => {
it('emits end event', async () => {
createComponent();
@@ -118,16 +132,29 @@ describe('DeletePackage', () => {
await clickOnButtonAndWait(eventPayload);
expect(createAlert).toHaveBeenCalledWith({
- message: DeletePackage.i18n.successMessage,
+ message: DeletePackages.i18n.successMessage,
variant: VARIANT_SUCCESS,
});
});
+
+ describe('when payload contains multiple packages', () => {
+ it('calls createAlert with success message when showSuccessAlert is true', async () => {
+ createComponent({ showSuccessAlert: true });
+
+ await clickOnButtonAndWait(eventPayloadMultiple);
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: DeletePackages.i18n.successMessageMultiple,
+ variant: VARIANT_SUCCESS,
+ });
+ });
+ });
});
describe.each`
errorType | mutationResolverResponse
${'connectionError'} | ${jest.fn().mockRejectedValue()}
- ${'localError'} | ${jest.fn().mockResolvedValue(packageDestroyMutationError())}
+ ${'localError'} | ${jest.fn().mockResolvedValue(packagesDestroyMutationError())}
`('on mutation $errorType', ({ mutationResolverResponse }) => {
beforeEach(() => {
mutationResolver = mutationResolverResponse;
@@ -147,11 +174,26 @@ describe('DeletePackage', () => {
await clickOnButtonAndWait(eventPayload);
expect(createAlert).toHaveBeenCalledWith({
- message: DeletePackage.i18n.errorMessage,
+ message: DeletePackages.i18n.errorMessage,
variant: VARIANT_WARNING,
captureError: true,
error: expect.any(Error),
});
});
+
+ describe('when payload contains multiple packages', () => {
+ it('calls createAlert with error message', async () => {
+ createComponent({ showSuccessAlert: true });
+
+ await clickOnButtonAndWait(eventPayloadMultiple);
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message: DeletePackages.i18n.errorMessageMultiple,
+ variant: VARIANT_WARNING,
+ captureError: true,
+ error: expect.any(Error),
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
index a7de751aadd..ec8e77fa923 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
@@ -54,12 +54,15 @@ exports[`packages_list_row renders 1`] = `
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-grow-1"
>
<div
- class="gl-display-flex"
+ class="gl-display-flex gl-align-items-center"
data-testid="left-secondary-infos"
>
- <span>
- 1.0.0
- </span>
+ <gl-truncate-stub
+ class="gl-max-w-15 gl-md-max-w-26"
+ position="end"
+ text="1.0.0"
+ withtooltip="true"
+ />
<!---->
@@ -135,18 +138,6 @@ exports[`packages_list_row renders 1`] = `
</div>
</div>
- <div
- class="gl-display-flex"
- >
- <div
- class="gl-w-7"
- />
-
- <!---->
-
- <div
- class="gl-w-9"
- />
- </div>
+ <!---->
</div>
`;
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
index bb04701a8b7..2a78cfb13f9 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
@@ -43,9 +43,11 @@ describe('packages_list_row', () => {
const findPackageLink = () => wrapper.findByTestId('details-link');
const findWarningIcon = () => wrapper.findByTestId('warning-icon');
const findLeftSecondaryInfos = () => wrapper.findByTestId('left-secondary-infos');
+ const findPackageVersion = () => findLeftSecondaryInfos().findComponent(GlTruncate);
const findPublishMethod = () => wrapper.findComponent(PublishMethod);
const findCreatedDateText = () => wrapper.findByTestId('created-date');
const findTimeAgoTooltip = () => wrapper.findComponent(TimeagoTooltip);
+ const findListItem = () => wrapper.findComponent(ListItem);
const findBulkDeleteAction = () => wrapper.findComponent(GlFormCheckbox);
const findPackageName = () => wrapper.findComponent(GlTruncate);
@@ -83,22 +85,13 @@ describe('packages_list_row', () => {
mountComponent();
expect(findPackageLink().props()).toMatchObject({
- event: 'click',
to: { name: 'details', params: { id: getIdFromGraphQLId(packageWithoutTags.id) } },
});
});
- it('does not have a link to navigate to the details page', () => {
- mountComponent({
- packageEntity: {
- ...packageWithoutTags,
- _links: {
- webPath: null,
- },
- },
- });
+ it('lists the package name', () => {
+ mountComponent();
- expect(findPackageLink().exists()).toBe(false);
expect(findPackageName().props()).toMatchObject({
text: '@gitlab-org/package-15',
});
@@ -155,11 +148,25 @@ describe('packages_list_row', () => {
describe(`when the package is in ${PACKAGE_ERROR_STATUS} status`, () => {
beforeEach(() => {
- mountComponent({ packageEntity: { ...packageWithoutTags, status: PACKAGE_ERROR_STATUS } });
+ mountComponent({
+ packageEntity: {
+ ...packageWithoutTags,
+ status: PACKAGE_ERROR_STATUS,
+ _links: {
+ webPath: null,
+ },
+ },
+ });
});
- it('details link is disabled', () => {
- expect(findPackageLink().props('event')).toBe('');
+ it('lists the package name', () => {
+ expect(findPackageName().props()).toMatchObject({
+ text: '@gitlab-org/package-15',
+ });
+ });
+
+ it('does not have a link to navigate to the details page', () => {
+ expect(findPackageLink().exists()).toBe(false);
});
it('has a warning icon', () => {
@@ -206,6 +213,9 @@ describe('packages_list_row', () => {
});
expect(findBulkDeleteAction().attributes('checked')).toBe('true');
+ expect(findListItem().props()).toMatchObject({
+ selected: true,
+ });
});
});
@@ -213,7 +223,10 @@ describe('packages_list_row', () => {
it('has the package version', () => {
mountComponent();
- expect(findLeftSecondaryInfos().text()).toContain(packageWithoutTags.version);
+ expect(findPackageVersion().props()).toMatchObject({
+ text: packageWithoutTags.version,
+ withTooltip: true,
+ });
});
it('if the pipeline exists show the author message', () => {
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
index 5e9cb8fbb0b..610640e0ca3 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
@@ -167,8 +167,8 @@ describe('packages_list', () => {
findPackageListDeleteModal().vm.$emit('ok');
});
- it('emits package:delete when modal confirms', () => {
- expect(wrapper.emitted('package:delete')[0]).toEqual([firstPackage]);
+ it('emits delete when modal confirms', () => {
+ expect(wrapper.emitted('delete')[0][0]).toEqual([firstPackage]);
});
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 9e9e08bc196..d897be1f344 100644
--- a/spec/frontend/packages_and_registries/package_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js
@@ -97,14 +97,22 @@ export const packageProject = () => ({
__typename: 'Project',
});
+export const linksData = {
+ _links: {
+ webPath: '/gitlab-org/package-15',
+ },
+};
+
export const packageVersions = () => [
{
createdAt: '2021-08-10T09:33:54Z',
id: 'gid://gitlab/Packages::Package/243',
name: '@gitlab-org/package-15',
status: 'DEFAULT',
+ canDestroy: true,
tags: { nodes: packageTags() },
version: '1.0.1',
+ ...linksData,
__typename: 'Package',
},
{
@@ -112,19 +120,14 @@ export const packageVersions = () => [
id: 'gid://gitlab/Packages::Package/244',
name: '@gitlab-org/package-15',
status: 'DEFAULT',
+ canDestroy: true,
tags: { nodes: packageTags() },
version: '1.0.2',
+ ...linksData,
__typename: 'Package',
},
];
-export const linksData = {
- _links: {
- webPath: '/gitlab-org/package-15',
- __typeName: 'PackageLinks',
- },
-};
-
export const packageData = (extend) => ({
__typename: 'Package',
id: 'gid://gitlab/Packages::Package/111',
@@ -294,14 +297,6 @@ export const packageMetadataQuery = (packageType) => {
};
};
-export const packageDestroyMutation = () => ({
- data: {
- destroyPackage: {
- errors: [],
- },
- },
-});
-
export const packagesDestroyMutation = () => ({
data: {
destroyPackages: {
@@ -329,25 +324,6 @@ export const packagesDestroyMutationError = () => ({
],
});
-export const packageDestroyMutationError = () => ({
- data: {
- destroyPackage: null,
- },
- errors: [
- {
- message:
- "The resource that you are attempting to access does not exist or you don't have permission to perform this action",
- locations: [
- {
- line: 2,
- column: 3,
- },
- ],
- path: ['destroyPackage'],
- },
- ],
-});
-
export const packageDestroyFilesMutation = () => ({
data: {
destroyPackageFiles: {
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 eb3b999c1ca..b494965a3cb 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
@@ -15,7 +15,7 @@ import InstallationCommands from '~/packages_and_registries/package_registry/com
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import PackageHistory from '~/packages_and_registries/package_registry/components/details/package_history.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue';
-import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
+import DeletePackages from '~/packages_and_registries/package_registry/components/functional/delete_packages.vue';
import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue';
import {
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
@@ -85,7 +85,7 @@ describe('PackagesApp', () => {
provide,
stubs: {
PackageTitle,
- DeletePackage,
+ DeletePackages,
GlModal: {
template: `
<div>
@@ -128,7 +128,8 @@ describe('PackagesApp', () => {
const findDependenciesCountBadge = () => wrapper.findByTestId('dependencies-badge');
const findNoDependenciesMessage = () => wrapper.findByTestId('no-dependencies-message');
const findDependencyRows = () => wrapper.findAllComponents(DependencyRow);
- const findDeletePackage = () => wrapper.findComponent(DeletePackage);
+ const findDeletePackageModal = () => wrapper.findAllComponents(DeletePackages).at(1);
+ const findDeletePackages = () => wrapper.findComponent(DeletePackages);
afterEach(() => {
wrapper.destroy();
@@ -267,7 +268,7 @@ describe('PackagesApp', () => {
await waitForPromises();
- findDeletePackage().vm.$emit('end');
+ findDeletePackageModal().vm.$emit('end');
expect(window.location.replace).toHaveBeenCalledWith(
'projectListUrl?showSuccessDeleteAlert=true',
@@ -281,7 +282,7 @@ describe('PackagesApp', () => {
await waitForPromises();
- findDeletePackage().vm.$emit('end');
+ findDeletePackageModal().vm.$emit('end');
expect(window.location.replace).toHaveBeenCalledWith(
'groupListUrl?showSuccessDeleteAlert=true',
@@ -595,13 +596,56 @@ describe('PackagesApp', () => {
it('binds the correct props', async () => {
const versionNodes = packageVersions();
- createComponent({ packageEntity: { versions: { nodes: versionNodes } } });
+ createComponent();
+
await waitForPromises();
expect(findVersionsList().props()).toMatchObject({
+ canDestroy: true,
versions: expect.arrayContaining(versionNodes),
});
});
+
+ describe('delete packages', () => {
+ it('exists and has the correct props', 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();
+
+ 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);
+
+ findDeletePackages().vm.$emit('end');
+
+ await nextTick();
+
+ expect(findVersionsList().props('isLoading')).toBe(false);
+ });
+ });
});
describe('dependency links', () => {
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
index b3cbd9f5dcf..a2ec527ce12 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -8,26 +8,18 @@ import ListPage from '~/packages_and_registries/package_registry/pages/list.vue'
import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue';
import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
import OriginalPackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
-import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
+import DeletePackages from '~/packages_and_registries/package_registry/components/functional/delete_packages.vue';
import {
PROJECT_RESOURCE_TYPE,
GROUP_RESOURCE_TYPE,
GRAPHQL_PAGE_SIZE,
EMPTY_LIST_HELP_URL,
PACKAGE_HELP_URL,
- DELETE_PACKAGES_ERROR_MESSAGE,
- DELETE_PACKAGES_SUCCESS_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import getPackagesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql';
import destroyPackagesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql';
-import {
- packagesListQuery,
- packageData,
- pagination,
- packagesDestroyMutation,
- packagesDestroyMutationError,
-} from '../mock_data';
+import { packagesListQuery, packageData, pagination } from '../mock_data';
jest.mock('~/flash');
@@ -53,12 +45,11 @@ describe('PackagesListApp', () => {
filters: { packageName: 'foo', packageType: 'CONAN' },
};
- const findAlert = () => wrapper.findComponent(GlAlert);
const findPackageTitle = () => wrapper.findComponent(PackageTitle);
const findSearch = () => wrapper.findComponent(PackageSearch);
const findListComponent = () => wrapper.findComponent(PackageList);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
- const findDeletePackage = () => wrapper.findComponent(DeletePackage);
+ const findDeletePackages = () => wrapper.findComponent(DeletePackages);
const mountComponent = ({
resolver = jest.fn().mockResolvedValue(packagesListQuery()),
@@ -82,7 +73,7 @@ describe('PackagesListApp', () => {
GlSprintf,
GlLink,
PackageList,
- DeletePackage,
+ DeletePackages,
},
});
};
@@ -243,26 +234,26 @@ describe('PackagesListApp', () => {
});
});
- describe('delete package', () => {
+ describe('delete packages', () => {
it('exists and has the correct props', async () => {
mountComponent();
await waitForFirstRequest();
- expect(findDeletePackage().props()).toMatchObject({
+ expect(findDeletePackages().props()).toMatchObject({
refetchQueries: [{ query: getPackagesQuery, variables: {} }],
showSuccessAlert: true,
});
});
- it('deletePackage is bound to package-list package:delete event', async () => {
+ it('deletePackages is bound to package-list delete event', async () => {
mountComponent();
await waitForFirstRequest();
- findListComponent().vm.$emit('package:delete', { id: 1 });
+ findListComponent().vm.$emit('delete', [{ id: 1 }]);
- expect(findDeletePackage().emitted('start')).toEqual([[]]);
+ expect(findDeletePackages().emitted('start')).toEqual([[]]);
});
it('start and end event set loading correctly', async () => {
@@ -270,59 +261,17 @@ describe('PackagesListApp', () => {
await waitForFirstRequest();
- findDeletePackage().vm.$emit('start');
+ findDeletePackages().vm.$emit('start');
await nextTick();
expect(findListComponent().props('isLoading')).toBe(true);
- findDeletePackage().vm.$emit('end');
+ findDeletePackages().vm.$emit('end');
await nextTick();
expect(findListComponent().props('isLoading')).toBe(false);
});
});
-
- describe('bulk delete package', () => {
- const items = [{ id: '1' }, { id: '2' }];
-
- it('calls mutation with the right values and shows success alert', async () => {
- const mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutation());
- mountComponent({
- mutationResolver,
- });
-
- await waitForFirstRequest();
-
- findListComponent().vm.$emit('delete', items);
-
- expect(mutationResolver).toHaveBeenCalledWith({
- ids: items.map((item) => item.id),
- });
-
- await waitForPromises();
-
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().props('variant')).toEqual('success');
- expect(findAlert().text()).toMatchInterpolatedText(DELETE_PACKAGES_SUCCESS_MESSAGE);
- });
-
- it('on error shows danger alert', async () => {
- const mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutationError());
- mountComponent({
- mutationResolver,
- });
-
- await waitForFirstRequest();
-
- findListComponent().vm.$emit('delete', items);
-
- await waitForPromises();
-
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().props('variant')).toEqual('danger');
- expect(findAlert().text()).toMatchInterpolatedText(DELETE_PACKAGES_ERROR_MESSAGE);
- });
- });
});
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
index daf0ee85fdf..0fbbf4ae58f 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
@@ -11,6 +11,7 @@ import {
KEEP_N_DUPLICATED_PACKAGE_FILES_LABEL,
KEEP_N_DUPLICATED_PACKAGE_FILES_DESCRIPTION,
} from '~/packages_and_registries/settings/project/constants';
+import packagesCleanupPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_packages_cleanup_policy.query.graphql';
import updatePackagesCleanupPolicyMutation from '~/packages_and_registries/settings/project/graphql/mutations/update_packages_cleanup_policy.mutation.graphql';
import Tracking from '~/tracking';
import { packagesCleanupPolicyPayload, packagesCleanupPolicyMutationPayload } from '../mock_data';
@@ -39,10 +40,13 @@ describe('Packages Cleanup Policy Settings Form', () => {
label: 'packages_cleanup_policies',
};
+ const defaultQueryResolver = jest.fn().mockResolvedValue(packagesCleanupPolicyPayload());
+
const findForm = () => wrapper.findComponent({ ref: 'form-element' });
const findSaveButton = () => wrapper.findByTestId('save-button');
const findKeepNDuplicatedPackageFilesDropdown = () =>
wrapper.findByTestId('keep-n-duplicated-package-files-dropdown');
+ const findNextRunAt = () => wrapper.findByTestId('next-run-at');
const submitForm = async () => {
findForm().trigger('submit');
@@ -77,10 +81,14 @@ describe('Packages Cleanup Policy Settings Form', () => {
const mountComponentWithApollo = ({
provide = defaultProvidedValues,
+ queryResolver = defaultQueryResolver,
mutationResolver,
queryPayload = packagesCleanupPolicyPayload(),
} = {}) => {
- const requestHandlers = [[updatePackagesCleanupPolicyMutation, mutationResolver]];
+ const requestHandlers = [
+ [updatePackagesCleanupPolicyMutation, mutationResolver],
+ [packagesCleanupPolicyQuery, queryResolver],
+ ];
fakeApollo = createMockApollo(requestHandlers);
@@ -160,6 +168,40 @@ describe('Packages Cleanup Policy Settings Form', () => {
});
});
+ describe('nextRunAt', () => {
+ it('when present renders time until next package cleanup', () => {
+ jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
+
+ mountComponent({
+ props: { value: { ...defaultProps.value, nextRunAt: '2063-04-04T02:42:00Z' } },
+ });
+
+ expect(findNextRunAt().text()).toMatchInterpolatedText(
+ 'Packages and assets will not be deleted until cleanup runs in about 2 hours.',
+ );
+ });
+
+ it('renders message for cleanup when its before current date', () => {
+ jest.spyOn(Date, 'now').mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
+
+ mountComponent({
+ props: { value: { ...defaultProps.value, nextRunAt: '2063-03-04T00:42:00Z' } },
+ });
+
+ expect(findNextRunAt().text()).toMatchInterpolatedText(
+ 'Packages and assets cleanup is ready to be executed when the next cleanup job runs.',
+ );
+ });
+
+ it('when null hides time until next package cleanup', () => {
+ mountComponent({
+ props: { value: { ...defaultProps.value, nextRunAt: null } },
+ });
+
+ expect(findNextRunAt().exists()).toBe(false);
+ });
+ });
+
describe('form', () => {
describe('actions', () => {
describe('submit button', () => {
@@ -209,7 +251,7 @@ describe('Packages Cleanup Policy Settings Form', () => {
});
describe('form submit event', () => {
- it('dispatches the correct apollo mutation', () => {
+ it('dispatches the correct apollo mutation and refetches query', async () => {
const mutationResolver = jest
.fn()
.mockResolvedValue(packagesCleanupPolicyMutationPayload());
@@ -225,6 +267,12 @@ describe('Packages Cleanup Policy Settings Form', () => {
projectPath: 'path',
},
});
+
+ await waitForPromises();
+
+ expect(defaultQueryResolver).toHaveBeenCalledWith({
+ projectPath: 'path',
+ });
});
it('tracks the submit event', () => {
@@ -251,6 +299,18 @@ describe('Packages Cleanup Policy Settings Form', () => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE);
});
+ it('shows error toast when mutation responds with errors', async () => {
+ mountComponentWithApollo({
+ mutationResolver: jest
+ .fn()
+ .mockResolvedValue(packagesCleanupPolicyMutationPayload({ errors: [new Error()] })),
+ });
+
+ await submitForm();
+
+ expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_ERROR_MESSAGE);
+ });
+
describe('when submit fails', () => {
it('shows an error', async () => {
mountComponentWithApollo({
diff --git a/spec/frontend/packages_and_registries/shared/components/registry_list_spec.js b/spec/frontend/packages_and_registries/shared/components/registry_list_spec.js
index aaca58d21bb..2e2d5e26d33 100644
--- a/spec/frontend/packages_and_registries/shared/components/registry_list_spec.js
+++ b/spec/frontend/packages_and_registries/shared/components/registry_list_spec.js
@@ -54,6 +54,28 @@ describe('Registry List', () => {
it('exists', () => {
expect(findSelectAll().exists()).toBe(true);
+ expect(findSelectAll().attributes('aria-label')).toBe('Select all');
+ expect(findSelectAll().attributes('disabled')).toBeUndefined();
+ expect(findSelectAll().attributes('indeterminate')).toBeUndefined();
+ });
+
+ it('sets disabled prop to true when items length is 0', () => {
+ mountComponent({ propsData: { ...defaultPropsData, items: [] } });
+
+ expect(findSelectAll().attributes('disabled')).toBe('true');
+ });
+
+ it('when few are selected, sets indeterminate prop to true', async () => {
+ await findScopedSlotSelectButton(0).trigger('click');
+
+ expect(findSelectAll().attributes('indeterminate')).toBe('true');
+ });
+
+ it('when all are selected, sets the right checkbox label', async () => {
+ findSelectAll().vm.$emit('change', true);
+ await nextTick();
+
+ expect(findSelectAll().attributes('aria-label')).toBe('Unselect all');
});
it('select and unselect all', async () => {
@@ -63,7 +85,7 @@ describe('Registry List', () => {
});
// simulate selection
- findSelectAll().vm.$emit('input', true);
+ findSelectAll().vm.$emit('change', true);
await nextTick();
// all rows selected
@@ -72,12 +94,12 @@ describe('Registry List', () => {
});
// simulate de-selection
- findSelectAll().vm.$emit('input', '');
+ findSelectAll().vm.$emit('change', false);
await nextTick();
// no row is not selected
items.forEach((item, index) => {
- expect(findScopedSlotIsSelectedValue(index).text()).toBe('');
+ expect(findScopedSlotIsSelectedValue(index).text()).toBe('false');
});
});
});