Commit 4b78a5d8 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Brandon Labuschagne

Add new package_files component

- source
- tests
parent 6de7a81f
......@@ -5,29 +5,26 @@ import {
GlModal,
GlModalDirective,
GlTooltipDirective,
GlLink,
GlEmptyState,
GlTab,
GlTabs,
GlTable,
GlSprintf,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import Tracking from '~/tracking';
import { s__ } from '~/locale';
import { objectToQueryString } from '~/lib/utils/common_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import PackageHistory from './package_history.vue';
import PackageTitle from './package_title.vue';
import PackagesListLoader from '../../shared/components/packages_list_loader.vue';
import PackageListRow from '../../shared/components/package_list_row.vue';
import { packageTypeToTrackCategory } from '../../shared/utils';
import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants';
import DependencyRow from './dependency_row.vue';
import AdditionalMetadata from './additional_metadata.vue';
import InstallationCommands from './installation_commands.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { __, s__ } from '~/locale';
import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants';
import { packageTypeToTrackCategory } from '../../shared/utils';
import { objectToQueryString } from '~/lib/utils/common_utils';
import PackageFiles from './package_files.vue';
export default {
name: 'PackagesApp',
......@@ -35,12 +32,9 @@ export default {
GlBadge,
GlButton,
GlEmptyState,
GlLink,
GlModal,
GlTab,
GlTabs,
GlTable,
FileIcon,
GlSprintf,
PackageTitle,
PackagesListLoader,
......@@ -49,12 +43,13 @@ export default {
PackageHistory,
AdditionalMetadata,
InstallationCommands,
PackageFiles,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
mixins: [timeagoMixin, Tracking.mixin()],
mixins: [Tracking.mixin()],
trackingActions: { ...TrackingActions },
computed: {
...mapState([
......@@ -72,14 +67,6 @@ export default {
isValidPackage() {
return Boolean(this.packageEntity.name);
},
filesTableRows() {
return this.packageFiles.map(x => ({
name: x.file_name,
downloadPath: x.download_path,
size: this.formatSize(x.size),
created: x.created_at,
}));
},
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
......@@ -128,22 +115,6 @@ export default {
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
},
filesTableHeaderFields: [
{
key: 'name',
label: __('Name'),
tdClass: 'd-flex align-items-center',
},
{
key: 'size',
label: __('Size'),
},
{
key: 'created',
label: __('Created'),
class: 'text-right',
},
],
};
</script>
......@@ -185,35 +156,11 @@ export default {
<additional-metadata :package-entity="packageEntity" />
</div>
<template v-if="showFiles">
<h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
<gl-table
:fields="$options.filesTableHeaderFields"
:items="filesTableRows"
tbody-tr-class="js-file-row"
>
<template #cell(name)="{ item }">
<gl-link
:href="item.downloadPath"
class="js-file-download gl-relative"
@click="track($options.trackingActions.PULL_PACKAGE)"
>
<file-icon
:file-name="item.name"
css-classes="gl-relative file-icon"
class="gl-mr-1 gl-relative"
/>
<span class="gl-relative">{{ item.name }}</span>
</gl-link>
</template>
<template #cell(created)="{ item }">
<span v-gl-tooltip :title="tooltipTitle(item.created)">{{
timeFormatted(item.created)
}}</span>
</template>
</gl-table>
</template>
<package-files
v-if="showFiles"
:package-files="packageFiles"
@download-file="track($options.trackingActions.PULL_PACKAGE)"
/>
</gl-tab>
<gl-tab v-if="showDependencies" title-item-class="js-dependencies-tab">
......
<script>
import { GlLink, GlTable } from '@gitlab/ui';
import { __ } from '~/locale';
import Tracking from '~/tracking';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
export default {
name: 'PackageFiles',
components: {
GlLink,
GlTable,
FileIcon,
TimeAgoTooltip,
},
mixins: [Tracking.mixin()],
props: {
packageFiles: {
type: Array,
required: false,
default: () => [],
},
},
computed: {
filesTableRows() {
return this.packageFiles.map(pf => ({
...pf,
size: this.formatSize(pf.size),
}));
},
},
methods: {
formatSize(size) {
return numberToHumanSize(size);
},
},
filesTableHeaderFields: [
{
key: 'name',
label: __('Name'),
tdClass: 'gl-display-flex gl-align-items-center',
},
{
key: 'size',
label: __('Size'),
},
{
key: 'created',
label: __('Created'),
class: 'gl-text-right',
},
],
};
</script>
<template>
<div>
<h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
<gl-table
:fields="$options.filesTableHeaderFields"
:items="filesTableRows"
:tbody-tr-attr="{ 'data-testid': 'file-row' }"
>
<template #cell(name)="{ item }">
<gl-link
:href="item.download_path"
class="gl-relative"
data-testid="download-link"
@click="$emit('download-file')"
>
<file-icon
:file-name="item.file_name"
css-classes="gl-relative file-icon"
class="gl-mr-1 gl-relative"
/>
<span class="gl-relative">{{ item.file_name }}</span>
</gl-link>
</template>
<template #cell(created)="{ item }">
<time-ago-tooltip :time="item.created_at" />
</template>
</gl-table>
</div>
</template>
......@@ -16,6 +16,7 @@ import DependencyRow from '~/packages/details/components/dependency_row.vue';
import PackageHistory from '~/packages/details/components/package_history.vue';
import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue';
import InstallationCommands from '~/packages/details/components/installation_commands.vue';
import PackageFiles from '~/packages/details/components/package_files.vue';
import {
composerPackage,
......@@ -23,7 +24,6 @@ import {
mavenPackage,
mavenFiles,
npmPackage,
npmFiles,
nugetPackage,
} from '../../mock_data';
......@@ -82,8 +82,6 @@ describe('PackagesApp', () => {
const packageTitle = () => wrapper.find(PackageTitle);
const emptyState = () => wrapper.find(GlEmptyState);
const allFileRows = () => wrapper.findAll('.js-file-row');
const firstFileDownloadLink = () => wrapper.find('.js-file-download');
const deleteButton = () => wrapper.find('.js-delete-button');
const deleteModal = () => wrapper.find(GlModal);
const modalDeleteButton = () => wrapper.find({ ref: 'modal-delete-button' });
......@@ -98,6 +96,7 @@ describe('PackagesApp', () => {
const findPackageHistory = () => wrapper.find(PackageHistory);
const findAdditionalMetadata = () => wrapper.find(AdditionalMetadata);
const findInstallationCommands = () => wrapper.find(InstallationCommands);
const findPackageFiles = () => wrapper.find(PackageFiles);
beforeEach(() => {
delete window.location;
......@@ -144,28 +143,7 @@ describe('PackagesApp', () => {
it('hides the files table if package type is COMPOSER', () => {
createComponent({ packageEntity: composerPackage });
expect(allFileRows().exists()).toBe(false);
});
it('renders a single file for an npm package as they only contain one file', () => {
createComponent({ packageEntity: npmPackage, packageFiles: npmFiles });
expect(allFileRows()).toExist();
expect(allFileRows()).toHaveLength(1);
});
it('renders multiple files for a package that contains more than one file', () => {
createComponent();
expect(allFileRows()).toExist();
expect(allFileRows()).toHaveLength(2);
});
it('allows the user to download a package file by rendering a download link', () => {
createComponent();
expect(allFileRows()).toExist();
expect(firstFileDownloadLink().vm.$attrs.href).toContain('download');
expect(findPackageFiles().exists()).toBe(false);
});
describe('deleting packages', () => {
......@@ -331,7 +309,7 @@ describe('PackagesApp', () => {
it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage });
firstFileDownloadLink().vm.$emit('click');
findPackageFiles().vm.$emit('download-file');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.PULL_PACKAGE,
......
import { mount } from '@vue/test-utils';
import stubChildren from 'helpers/stub_children';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import component from '~/packages/details/components/package_files.vue';
import { npmFiles, mavenFiles } from '../../mock_data';
describe('Package Files', () => {
let wrapper;
const findAllRows = () => wrapper.findAll('[data-testid="file-row"');
const findFirstRow = () => findAllRows().at(0);
const findFirstRowDownloadLink = () => findFirstRow().find('[data-testid="download-link"');
const findFirstRowFileIcon = () => findFirstRow().find(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().find(TimeAgoTooltip);
const createComponent = (packageFiles = npmFiles) => {
wrapper = mount(component, {
propsData: {
packageFiles,
},
stubs: {
...stubChildren(component),
GlTable: false,
GlLink: '<div><slot></slot></div>',
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('rows', () => {
it('renders a single file for an npm package', () => {
createComponent();
expect(findAllRows()).toHaveLength(1);
});
it('renders multiple files for a package that contains more than one file', () => {
createComponent(mavenFiles);
expect(findAllRows()).toHaveLength(2);
});
});
describe('link', () => {
it('exists', () => {
createComponent();
expect(findFirstRowDownloadLink().exists()).toBe(true);
});
it('has the correct attrs bound', () => {
createComponent();
expect(findFirstRowDownloadLink().attributes('href')).toBe(npmFiles[0].download_path);
});
it('emits "download-file" event on click', () => {
createComponent();
findFirstRowDownloadLink().vm.$emit('click');
expect(wrapper.emitted('download-file')).toEqual([[]]);
});
});
describe('file-icon', () => {
it('exists', () => {
createComponent();
expect(findFirstRowFileIcon().exists()).toBe(true);
});
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowFileIcon().props('fileName')).toBe(npmFiles[0].file_name);
});
});
describe('time-ago tooltip', () => {
it('exists', () => {
createComponent();
expect(findFirstRowCreatedAt().exists()).toBe(true);
});
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowCreatedAt().props('time')).toBe(npmFiles[0].created_at);
});
});
});
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