Commit 4cc524ca authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '330847-convert-package-details-page-to-use-graphql' into 'master'

Enable delete of package in refactored package details

See merge request gitlab-org/gitlab!67604
parents ec810919 3d59ceb2
......@@ -39,7 +39,9 @@ import {
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
SHOW_DELETE_SUCCESS_ALERT,
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import Tracking from '~/tracking';
......@@ -156,9 +158,24 @@ export default {
// this.fetchPackageVersions();
}
},
deletePackage() {
return this.$apollo
.mutate({
mutation: destroyPackageMutation,
variables: {
id: this.packageEntity.id,
},
})
.then(({ data }) => {
if (data?.destroyPackage?.errors[0]) {
throw data.destroyPackage.errors[0];
}
});
},
async confirmPackageDeletion() {
this.track(DELETE_PACKAGE_TRACKING_ACTION);
try {
await this.deletePackage();
const returnTo =
......@@ -169,6 +186,14 @@ export default {
const modalQuery = objectToQuery({ [SHOW_DELETE_SUCCESS_ALERT]: true });
window.location.replace(`${returnTo}?${modalQuery}`);
} catch (error) {
createFlash({
message: DELETE_PACKAGE_ERROR_MESSAGE,
type: 'warning',
captureError: true,
error,
});
}
},
handleFileDelete(file) {
this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION);
......@@ -225,10 +250,10 @@ export default {
<gl-button
v-if="canDelete"
v-gl-modal="'delete-modal'"
class="js-delete-button"
variant="danger"
category="primary"
data-qa-selector="delete_button"
data-testid="delete-package"
>
{{ __('Delete') }}
</gl-button>
......@@ -303,7 +328,6 @@ export default {
<gl-modal
ref="deleteModal"
class="js-delete-modal"
modal-id="delete-modal"
:action-primary="$options.modal.packageDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
......
mutation destroyPackage($id: PackagesPackageID!) {
destroyPackage(input: { id: $id }) {
errors
}
}
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlModal } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
......@@ -10,11 +12,22 @@ import PackagesApp from '~/packages_and_registries/package_registry/components/d
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.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 { FETCH_PACKAGE_DETAILS_ERROR_MESSAGE } from '~/packages_and_registries/package_registry/constants';
import {
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import { packageDetailsQuery, packageData, emptyPackageDetailsQuery } from '../../mock_data';
import {
packageDetailsQuery,
packageData,
emptyPackageDetailsQuery,
packageDestroyMutation,
packageDestroyMutationError,
} from '../../mock_data';
jest.mock('~/flash');
useMockLocationHelper();
const localVue = createLocalVue();
......@@ -34,16 +47,23 @@ describe('PackagesApp', () => {
groupListUrl: 'groupListUrl',
};
function createComponent({ resolver = jest.fn().mockResolvedValue(packageDetailsQuery()) } = {}) {
function createComponent({
resolver = jest.fn().mockResolvedValue(packageDetailsQuery()),
mutationResolver = jest.fn().mockResolvedValue(packageDestroyMutation()),
} = {}) {
localVue.use(VueApollo);
const requestHandlers = [[getPackageDetails, resolver]];
const requestHandlers = [
[getPackageDetails, resolver],
[destroyPackageMutation, mutationResolver],
];
apolloProvider = createMockApollo(requestHandlers);
wrapper = shallowMount(PackagesApp, {
wrapper = shallowMountExtended(PackagesApp, {
localVue,
apolloProvider,
provide,
stubs: { PackageTitle },
});
}
......@@ -52,6 +72,8 @@ describe('PackagesApp', () => {
const findPackageHistory = () => wrapper.findComponent(PackageHistory);
const findAdditionalMetadata = () => wrapper.findComponent(AdditionalMetadata);
const findInstallationCommands = () => wrapper.findComponent(InstallationCommands);
const findDeleteModal = () => wrapper.findComponent(GlModal);
const findDeleteButton = () => wrapper.findByTestId('delete-package');
afterEach(() => {
wrapper.destroy();
......@@ -121,4 +143,101 @@ describe('PackagesApp', () => {
packageEntity: expect.objectContaining(packageData()),
});
});
describe('delete package', () => {
const originalReferrer = document.referrer;
const setReferrer = (value = provide.projectName) => {
Object.defineProperty(document, 'referrer', {
value,
configurable: true,
});
};
const performDeletePackage = async () => {
await findDeleteButton().trigger('click');
findDeleteModal().vm.$emit('primary');
await waitForPromises();
};
afterEach(() => {
Object.defineProperty(document, 'referrer', {
value: originalReferrer,
configurable: true,
});
});
it('shows the delete confirmation modal when delete is clicked', async () => {
createComponent();
await waitForPromises();
await findDeleteButton().trigger('click');
expect(findDeleteModal().exists()).toBe(true);
});
describe('successful request', () => {
it('when referrer contains project name calls window.replace with project url', async () => {
setReferrer();
createComponent();
await waitForPromises();
await performDeletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'projectListUrl?showSuccessDeleteAlert=true',
);
});
it('when referrer does not contain project name calls window.replace with group url', async () => {
setReferrer('baz');
createComponent();
await waitForPromises();
await performDeletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'groupListUrl?showSuccessDeleteAlert=true',
);
});
});
describe('request failure', () => {
it('on global failure it displays an alert', async () => {
createComponent({ mutationResolver: jest.fn().mockRejectedValue() });
await waitForPromises();
await performDeletePackage();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_ERROR_MESSAGE,
}),
);
});
it('on payload with error it displays an alert', async () => {
createComponent({
mutationResolver: jest.fn().mockResolvedValue(packageDestroyMutationError()),
});
await waitForPromises();
await performDeletePackage();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_ERROR_MESSAGE,
}),
);
});
});
});
});
......@@ -125,3 +125,29 @@ export const emptyPackageDetailsQuery = () => ({
},
},
});
export const packageDestroyMutation = () => ({
data: {
destroyPackage: {
errors: [],
},
},
});
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'],
},
],
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment