Commit 7df4f855 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Natalia Tepluhina

Refactor and enable package files in package details

parent 9a8c2097
......@@ -20,13 +20,14 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import { s__, __ } from '~/locale';
// import DependencyRow from '~/packages/details/components/dependency_row.vue';
// import PackageFiles from '~/packages/details/components/package_files.vue';
// import PackageListRow from '~/packages/shared/components/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
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 {
PACKAGE_TYPE_NUGET,
PACKAGE_TYPE_COMPOSER,
......@@ -40,8 +41,12 @@ import {
SHOW_DELETE_SUCCESS_ALERT,
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
DELETE_PACKAGE_FILE_ERROR_MESSAGE,
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import destroyPackageFileMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import Tracking from '~/tracking';
......@@ -55,17 +60,14 @@ export default {
GlTab,
GlTabs,
GlSprintf,
PackageTitle: () =>
import('~/packages_and_registries/package_registry/components/details/package_title.vue'),
TerraformTitle: () =>
import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'),
PackageTitle,
PackagesListLoader,
// PackageListRow,
// DependencyRow,
PackageHistory,
AdditionalMetadata,
InstallationCommands,
// PackageFiles,
PackageFiles,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -74,7 +76,6 @@ export default {
mixins: [Tracking.mixin()],
inject: [
'packageId',
'titleComponent',
'projectName',
'canDelete',
'svgPath',
......@@ -123,7 +124,7 @@ export default {
};
},
packageFiles() {
return this.packageEntity.packageFiles;
return this.packageEntity?.packageFiles?.nodes;
},
isLoading() {
return this.$apollo.queries.packageEntity.loading;
......@@ -133,7 +134,7 @@ export default {
},
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
category: packageTypeToTrackCategory(this.packageEntity.packageType),
};
},
hasVersions() {
......@@ -143,10 +144,10 @@ export default {
return this.packageEntity.dependency_links || [];
},
showDependencies() {
return this.packageEntity.package_type === PACKAGE_TYPE_NUGET;
return this.packageEntity.packageType === PACKAGE_TYPE_NUGET;
},
showFiles() {
return this.packageEntity?.package_type !== PACKAGE_TYPE_COMPOSER;
return this.packageEntity?.packageType !== PACKAGE_TYPE_COMPOSER;
},
},
methods: {
......@@ -158,19 +159,17 @@ 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 deletePackage() {
const { data } = await this.$apollo.mutate({
mutation: destroyPackageMutation,
variables: {
id: this.packageEntity.id,
},
});
if (data?.destroyPackage?.errors[0]) {
throw data.destroyPackage.errors[0];
}
},
async confirmPackageDeletion() {
this.track(DELETE_PACKAGE_TRACKING_ACTION);
......@@ -195,6 +194,37 @@ export default {
});
}
},
async deletePackageFile(id) {
try {
const { data } = await this.$apollo.mutate({
mutation: destroyPackageFileMutation,
variables: {
id,
},
awaitRefetchQueries: true,
refetchQueries: [
{
query: getPackageDetails,
variables: this.queryVariables,
},
],
});
if (data?.destroyPackageFile?.errors[0]) {
throw data.destroyPackageFile.errors[0];
}
createFlash({
message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
type: 'success',
});
} catch (error) {
createFlash({
message: DELETE_PACKAGE_FILE_ERROR_MESSAGE,
type: 'warning',
captureError: true,
error,
});
}
},
handleFileDelete(file) {
this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION);
this.fileToDelete = { ...file };
......@@ -202,7 +232,7 @@ export default {
},
confirmFileDelete() {
this.track(DELETE_PACKAGE_FILE_TRACKING_ACTION);
// this.deletePackageFile(this.fileToDelete.id);
this.deletePackageFile(this.fileToDelete.id);
this.fileToDelete = null;
},
},
......@@ -245,7 +275,7 @@ export default {
/>
<div v-else class="packages-app">
<component :is="titleComponent" :package-entity="packageEntity">
<package-title :package-entity="packageEntity">
<template #delete-button>
<gl-button
v-if="canDelete"
......@@ -258,7 +288,7 @@ export default {
{{ __('Delete') }}
</gl-button>
</template>
</component>
</package-title>
<gl-tabs>
<gl-tab :title="__('Detail')">
......@@ -270,13 +300,12 @@ export default {
<additional-metadata :package-entity="packageEntity" />
</div>
<!-- <package-files
<package-files
v-if="showFiles"
:package-files="packageFiles"
:can-delete="canDelete"
@download-file="track($options.trackingActions.PULL_PACKAGE)"
@delete-file="handleFileDelete"
/> -->
/>
</gl-tab>
<gl-tab v-if="showDependencies" title-item-class="js-dependencies-tab">
......@@ -288,11 +317,11 @@ export default {
</template>
<template v-if="packageDependencies.length > 0">
<dependency-row
<!-- <dependency-row
v-for="(dep, index) in packageDependencies"
:key="index"
:dependency="dep"
/>
/> -->
</template>
<p v-else class="gl-mt-3" data-testid="no-dependencies-message">
......@@ -329,6 +358,7 @@ export default {
<gl-modal
ref="deleteModal"
modal-id="delete-modal"
data-testid="delete-modal"
:action-primary="$options.modal.packageDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
@primary="confirmPackageDeletion"
......@@ -351,6 +381,7 @@ export default {
modal-id="delete-file-modal"
:action-primary="$options.modal.fileDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
data-testid="delete-file-modal"
@primary="confirmFileDelete"
@canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)"
>
......
......@@ -22,17 +22,13 @@ export default {
FileSha,
},
mixins: [Tracking.mixin()],
inject: ['canDelete'],
props: {
packageFiles: {
type: Array,
required: false,
default: () => [],
},
canDelete: {
type: Boolean,
default: false,
required: false,
},
},
computed: {
filesTableRows() {
......@@ -43,6 +39,8 @@ export default {
}));
},
showCommitColumn() {
// note that this is always false for now since we do not return
// pipelines associated to files for performance concerns
return this.filesTableRows.some((row) => Boolean(row.pipeline?.id));
},
filesTableHeaderFields() {
......@@ -80,7 +78,7 @@ export default {
return numberToHumanSize(size);
},
hasDetails(item) {
return item.file_sha256 || item.file_md5 || item.file_sha1;
return item.fileSha256 || item.fileMd5 || item.fileSha1;
},
},
i18n: {
......@@ -107,32 +105,32 @@ export default {
@click="toggleDetails"
/>
<gl-link
:href="item.download_path"
:href="item.downloadPath"
class="gl-text-gray-500"
data-testid="download-link"
@click="$emit('download-file')"
>
<file-icon
:file-name="item.file_name"
:file-name="item.fileName"
css-classes="gl-relative file-icon"
class="gl-mr-1 gl-relative"
/>
<span>{{ item.file_name }}</span>
<span>{{ item.fileName }}</span>
</gl-link>
</template>
<template #cell(commit)="{ item }">
<gl-link
v-if="item.pipeline && item.pipeline.project"
:href="item.pipeline.project.commit_url"
v-if="item.pipeline && item.pipeline"
:href="item.pipeline.commitPath"
class="gl-text-gray-500"
data-testid="commit-link"
>{{ item.pipeline.git_commit_message }}</gl-link
>{{ item.pipeline.sha }}</gl-link
>
</template>
<template #cell(created)="{ item }">
<time-ago-tooltip :time="item.created_at" />
<time-ago-tooltip :time="item.createdAt" />
</template>
<template #cell(actions)="{ item }">
......@@ -151,13 +149,13 @@ export default {
class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100"
>
<file-sha
v-if="item.file_sha256"
v-if="item.fileSha256"
data-testid="sha-256"
title="SHA-256"
:sha="item.file_sha256"
:sha="item.fileSha256"
/>
<file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" />
<file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" />
<file-sha v-if="item.fileMd5" data-testid="md5" title="MD5" :sha="item.fileMd5" />
<file-sha v-if="item.fileSha1" data-testid="sha-1" title="SHA-1" :sha="item.fileSha1" />
</div>
</template>
</gl-table>
......
mutation destroyPackageFile($id: PackagesPackageFileID!) {
destroyPackageFile(input: { id: $id }) {
errors
}
}
......@@ -38,6 +38,8 @@ query getPackageDetails($id: ID!) {
fileSha1
fileSha256
size
createdAt
downloadPath
}
}
metadata {
......
......@@ -18,7 +18,6 @@ export default () => {
apolloProvider,
provide: {
canDelete: parseBoolean(canDelete),
titleComponent: 'PackageTitle',
...datasetOptions,
},
render(createElement) {
......
import { GlEmptyState, GlModal } from '@gitlab/ui';
import { GlEmptyState } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
......@@ -10,13 +11,19 @@ import createFlash from '~/flash';
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import PackagesApp from '~/packages_and_registries/package_registry/components/details/app.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
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 {
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
DELETE_PACKAGE_ERROR_MESSAGE,
PACKAGE_TYPE_COMPOSER,
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
DELETE_PACKAGE_FILE_ERROR_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import destroyPackageMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package.mutation.graphql';
import destroyPackageFileMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_package_file.mutation.graphql';
import getPackageDetails from '~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql';
import {
packageDetailsQuery,
......@@ -24,6 +31,9 @@ import {
emptyPackageDetailsQuery,
packageDestroyMutation,
packageDestroyMutationError,
packageFiles,
packageDestroyFileMutation,
packageDestroyFileMutationError,
} from '../../mock_data';
jest.mock('~/flash');
......@@ -50,12 +60,14 @@ describe('PackagesApp', () => {
function createComponent({
resolver = jest.fn().mockResolvedValue(packageDetailsQuery()),
mutationResolver = jest.fn().mockResolvedValue(packageDestroyMutation()),
fileDeleteMutationResolver = jest.fn().mockResolvedValue(packageDestroyFileMutation()),
} = {}) {
localVue.use(VueApollo);
const requestHandlers = [
[getPackageDetails, resolver],
[destroyPackageMutation, mutationResolver],
[destroyPackageFileMutation, fileDeleteMutationResolver],
];
apolloProvider = createMockApollo(requestHandlers);
......@@ -63,7 +75,15 @@ describe('PackagesApp', () => {
localVue,
apolloProvider,
provide,
stubs: { PackageTitle },
stubs: {
PackageTitle,
GlModal: {
template: '<div></div>',
methods: {
show: jest.fn(),
},
},
},
});
}
......@@ -72,8 +92,10 @@ describe('PackagesApp', () => {
const findPackageHistory = () => wrapper.findComponent(PackageHistory);
const findAdditionalMetadata = () => wrapper.findComponent(AdditionalMetadata);
const findInstallationCommands = () => wrapper.findComponent(InstallationCommands);
const findDeleteModal = () => wrapper.findComponent(GlModal);
const findDeleteModal = () => wrapper.findByTestId('delete-modal');
const findDeleteButton = () => wrapper.findByTestId('delete-package');
const findPackageFiles = () => wrapper.findComponent(PackageFiles);
const findDeleteFileModal = () => wrapper.findByTestId('delete-file-modal');
afterEach(() => {
wrapper.destroy();
......@@ -240,4 +262,104 @@ describe('PackagesApp', () => {
});
});
});
describe('package files', () => {
it('renders the package files component and has the right props', async () => {
const expectedFile = { ...packageFiles()[0] };
// eslint-disable-next-line no-underscore-dangle
delete expectedFile.__typename;
createComponent();
await waitForPromises();
expect(findPackageFiles().exists()).toBe(true);
expect(findPackageFiles().props('packageFiles')[0]).toMatchObject(expectedFile);
});
it('does not render the package files table when the package is composer', async () => {
createComponent({
resolver: jest
.fn()
.mockResolvedValue(packageDetailsQuery({ packageType: PACKAGE_TYPE_COMPOSER })),
});
await waitForPromises();
expect(findPackageFiles().exists()).toBe(false);
});
describe('deleting a file', () => {
const [fileToDelete] = packageFiles();
const doDeleteFile = () => {
findPackageFiles().vm.$emit('delete-file', fileToDelete);
findDeleteFileModal().vm.$emit('primary');
return waitForPromises();
};
it('opens a confirmation modal', async () => {
createComponent();
await waitForPromises();
findPackageFiles().vm.$emit('delete-file', fileToDelete);
await nextTick();
expect(findDeleteFileModal().exists()).toBe(true);
});
it('confirming on the modal deletes the file and shows a success message', async () => {
const resolver = jest.fn().mockResolvedValue(packageDetailsQuery());
createComponent({ resolver });
await waitForPromises();
await doDeleteFile();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
}),
);
// we are re-fetching the package details, so we expect the resolver to have been called twice
expect(resolver).toHaveBeenCalledTimes(2);
});
describe('errors', () => {
it('shows an error when the mutation request fails', async () => {
createComponent({ fileDeleteMutationResolver: jest.fn().mockRejectedValue() });
await waitForPromises();
await doDeleteFile();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_FILE_ERROR_MESSAGE,
}),
);
});
it('shows an error when the mutation request returns an error payload', async () => {
createComponent({
fileDeleteMutationResolver: jest
.fn()
.mockResolvedValue(packageDestroyFileMutationError()),
});
await waitForPromises();
await doDeleteFile();
expect(createFlash).toHaveBeenCalledWith(
expect.objectContaining({
message: DELETE_PACKAGE_FILE_ERROR_MESSAGE,
}),
);
});
});
});
});
});
import { GlDropdown, GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import stubChildren from 'helpers/stub_children';
import { npmFiles, mavenFiles } from 'jest/packages/mock_data';
import component from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
import { packageFiles as packageFilesMock } from 'jest/packages_and_registries/package_registry/mock_data';
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
describe('Package Files', () => {
let wrapper;
const findAllRows = () => wrapper.findAll('[data-testid="file-row"');
const findFirstRow = () => findAllRows().at(0);
const findSecondRow = () => findAllRows().at(1);
const findFirstRowDownloadLink = () => findFirstRow().find('[data-testid="download-link"]');
const findFirstRowCommitLink = () => findFirstRow().find('[data-testid="commit-link"]');
const findSecondRowCommitLink = () => findSecondRow().find('[data-testid="commit-link"]');
const findFirstRowFileIcon = () => findFirstRow().find(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().find(TimeAgoTooltip);
const findFirstActionMenu = () => findFirstRow().findComponent(GlDropdown);
const findActionMenuDelete = () => findFirstActionMenu().find('[data-testid="delete-file"]');
const findAllRows = () => wrapper.findAllByTestId('file-row');
const findFirstRow = () => extendedWrapper(findAllRows().at(0));
const findSecondRow = () => extendedWrapper(findAllRows().at(1));
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
const findFirstRowCommitLink = () => findFirstRow().findByTestId('commit-link');
const findSecondRowCommitLink = () => findSecondRow().findByTestId('commit-link');
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
const findActionMenuDelete = () => findFirstActionMenu().findByTestId('delete-file');
const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
const findFirstRowShaComponent = (id) => wrapper.find(`[data-testid="${id}"]`);
const findFirstRowShaComponent = (id) => wrapper.findByTestId(id);
const createComponent = ({ packageFiles = npmFiles, canDelete = true } = {}) => {
wrapper = mount(component, {
const files = packageFilesMock();
const [file] = files;
const createComponent = ({ packageFiles = [file], canDelete = true } = {}) => {
wrapper = mountExtended(PackageFiles, {
provide: { canDelete },
propsData: {
packageFiles,
canDelete,
},
stubs: {
...stubChildren(component),
...stubChildren(PackageFiles),
GlTable: false,
},
});
......@@ -38,7 +41,6 @@ describe('Package Files', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('rows', () => {
......@@ -49,7 +51,7 @@ describe('Package Files', () => {
});
it('renders multiple files for a package that contains more than one file', () => {
createComponent({ packageFiles: mavenFiles });
createComponent({ packageFiles: files });
expect(findAllRows()).toHaveLength(2);
});
......@@ -65,7 +67,7 @@ describe('Package Files', () => {
it('has the correct attrs bound', () => {
createComponent();
expect(findFirstRowDownloadLink().attributes('href')).toBe(npmFiles[0].download_path);
expect(findFirstRowDownloadLink().attributes('href')).toBe(file.downloadPath);
});
it('emits "download-file" event on click', () => {
......@@ -87,7 +89,7 @@ describe('Package Files', () => {
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowFileIcon().props('fileName')).toBe(npmFiles[0].file_name);
expect(findFirstRowFileIcon().props('fileName')).toBe(file.fileName);
});
});
......@@ -101,35 +103,47 @@ describe('Package Files', () => {
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowCreatedAt().props('time')).toBe(npmFiles[0].created_at);
expect(findFirstRowCreatedAt().props('time')).toBe(file.createdAt);
});
});
describe('commit', () => {
const withPipeline = {
...file,
pipelines: [
{
sha: 'sha',
id: 1,
commitPath: 'commitPath',
},
],
};
describe('when package file has a pipeline associated', () => {
it('exists', () => {
createComponent();
createComponent({ packageFiles: [withPipeline] });
expect(findFirstRowCommitLink().exists()).toBe(true);
});
it('the link points to the commit url', () => {
createComponent();
it('the link points to the commit path', () => {
createComponent({ packageFiles: [withPipeline] });
expect(findFirstRowCommitLink().attributes('href')).toBe(
npmFiles[0].pipelines[0].project.commit_url,
withPipeline.pipelines[0].commitPath,
);
});
it('the text is git_commit_message', () => {
createComponent();
it('the text is the pipeline sha', () => {
createComponent({ packageFiles: [withPipeline] });
expect(findFirstRowCommitLink().text()).toBe(npmFiles[0].pipelines[0].git_commit_message);
expect(findFirstRowCommitLink().text()).toBe(withPipeline.pipelines[0].sha);
});
});
describe('when package file has no pipeline associated', () => {
it('does not exist', () => {
createComponent({ packageFiles: mavenFiles });
createComponent();
expect(findFirstRowCommitLink().exists()).toBe(false);
});
......@@ -137,7 +151,7 @@ describe('Package Files', () => {
describe('when only one file lacks an associated pipeline', () => {
it('renders the commit when it exists and not otherwise', () => {
createComponent({ packageFiles: [npmFiles[0], mavenFiles[0]] });
createComponent({ packageFiles: [withPipeline, file] });
expect(findFirstRowCommitLink().exists()).toBe(true);
expect(findSecondRowCommitLink().exists()).toBe(false);
......@@ -166,7 +180,7 @@ describe('Package Files', () => {
findActionMenuDelete().vm.$emit('click');
const [[{ id }]] = wrapper.emitted('delete-file');
expect(id).toBe(npmFiles[0].id);
expect(id).toBe(file.id);
});
});
});
......@@ -193,10 +207,10 @@ describe('Package Files', () => {
});
it('is hidden when no details is present', () => {
const [{ ...noShaFile }] = npmFiles;
noShaFile.file_sha256 = null;
noShaFile.file_md5 = null;
noShaFile.file_sha1 = null;
const { ...noShaFile } = file;
noShaFile.fileSha256 = null;
noShaFile.fileMd5 = null;
noShaFile.fileSha1 = null;
createComponent({ packageFiles: [noShaFile] });
expect(findFirstToggleDetailsButton().exists()).toBe(false);
......@@ -229,9 +243,9 @@ describe('Package Files', () => {
it.each`
selector | title | sha
${'sha-256'} | ${'SHA-256'} | ${'file_sha256'}
${'md5'} | ${'MD5'} | ${'file_md5'}
${'sha-1'} | ${'SHA-1'} | ${'file_sha1'}
${'sha-256'} | ${'SHA-256'} | ${'fileSha256'}
${'md5'} | ${'MD5'} | ${'fileMd5'}
${'sha-1'} | ${'SHA-1'} | ${'be93151dc23ac34a82752444556fe79b32c7a1ad'}
`('has a $title row', async ({ selector, title, sha }) => {
createComponent();
......@@ -244,8 +258,8 @@ describe('Package Files', () => {
});
it('does not display a row when the data is missing', async () => {
const [{ ...missingMd5 }] = npmFiles;
missingMd5.file_md5 = null;
const { ...missingMd5 } = file;
missingMd5.fileMd5 = null;
createComponent({ packageFiles: [missingMd5] });
......
......@@ -29,11 +29,13 @@ export const packagePipelines = (extend) => [
export const packageFiles = () => [
{
id: 'gid://gitlab/Packages::PackageFile/118',
fileMd5: null,
fileMd5: 'fileMd5',
fileName: 'foo-1.0.1.tgz',
fileSha1: 'be93151dc23ac34a82752444556fe79b32c7a1ad',
fileSha256: null,
fileSha256: 'fileSha256',
size: '409600',
createdAt: '2020-08-17T14:23:32Z',
downloadPath: 'downloadPath',
__typename: 'PackageFile',
},
{
......@@ -43,6 +45,8 @@ export const packageFiles = () => [
fileSha1: 'be93151dc23ac34a82752444556fe79b32c7a1ss',
fileSha256: null,
size: '409600',
createdAt: '2020-08-17T14:23:32Z',
downloadPath: 'downloadPath',
__typename: 'PackageFile',
},
];
......@@ -90,7 +94,7 @@ export const nugetMetadata = () => ({
projectUrl: 'projectUrl',
});
export const packageDetailsQuery = () => ({
export const packageDetailsQuery = (extendPackage) => ({
data: {
package: {
...packageData(),
......@@ -114,6 +118,7 @@ export const packageDetailsQuery = () => ({
__typename: 'PackageFileConnection',
},
__typename: 'PackageDetailsType',
...extendPackage,
},
},
});
......@@ -133,6 +138,7 @@ export const packageDestroyMutation = () => ({
},
},
});
export const packageDestroyMutationError = () => ({
data: {
destroyPackage: null,
......@@ -151,3 +157,29 @@ export const packageDestroyMutationError = () => ({
},
],
});
export const packageDestroyFileMutation = () => ({
data: {
destroyPackageFile: {
errors: [],
},
},
});
export const packageDestroyFileMutationError = () => ({
data: {
destroyPackageFile: 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: ['destroyPackageFile'],
},
],
});
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