Commit 31f03145 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Natalia Tepluhina

Move details_row to shared folder

- source
- tests
- bindings
parent 939b05db
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import DetailsRow from '~/registry/shared/components/details_row.vue';
import { generateConanRecipe } from '../utils';
import { PackageType } from '../../shared/constants';
export default {
i18n: {
sourceText: s__('PackageRegistry|Source project located at %{link}'),
licenseText: s__('PackageRegistry|License information located at %{link}'),
recipeText: s__('PackageRegistry|Recipe: %{recipe}'),
appGroup: s__('PackageRegistry|App group: %{group}'),
appName: s__('PackageRegistry|App name: %{name}'),
},
components: {
DetailsRow,
GlLink,
GlSprintf,
},
props: {
packageEntity: {
type: Object,
required: true,
},
},
computed: {
conanRecipe() {
return generateConanRecipe(this.packageEntity);
},
showMetadata() {
const visibilityConditions = {
[PackageType.NUGET]: this.packageEntity.nuget_metadatum,
[PackageType.CONAN]: this.packageEntity.conan_metadatum,
[PackageType.MAVEN]: this.packageEntity.maven_metadatum,
};
return visibilityConditions[this.packageEntity.package_type];
},
},
};
</script>
<template>
<div v-if="showMetadata">
<h3 class="gl-font-lg gl-mt-5" data-testid="title">{{ __('Additional Metadata') }}</h3>
<div class="gl-bg-gray-50 gl-inset-border-1-gray-100 gl-rounded-base" data-testid="main">
<template v-if="packageEntity.nuget_metadatum">
<details-row icon="project" padding="gl-p-4" dashed data-testid="nuget-source">
<gl-sprintf :message="$options.i18n.sourceText">
<template #link>
<gl-link :href="packageEntity.nuget_metadatum.project_url" target="_blank">{{
packageEntity.nuget_metadatum.project_url
}}</gl-link>
</template>
</gl-sprintf>
</details-row>
<details-row icon="license" padding="gl-p-4" data-testid="nuget-license">
<gl-sprintf :message="$options.i18n.licenseText">
<template #link>
<gl-link :href="packageEntity.nuget_metadatum.license_url" target="_blank">{{
packageEntity.nuget_metadatum.license_url
}}</gl-link>
</template>
</gl-sprintf>
</details-row>
</template>
<details-row
v-else-if="packageEntity.conan_metadatum"
icon="information-o"
padding="gl-p-4"
data-testid="conan-recipe"
>
<gl-sprintf :message="$options.i18n.recipeText">
<template #recipe>{{ conanRecipe }}</template>
</gl-sprintf>
</details-row>
<template v-else-if="packageEntity.maven_metadatum">
<details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app">
<gl-sprintf :message="$options.i18n.appName">
<template #name>
<strong>{{ packageEntity.maven_metadatum.app_name }}</strong>
</template>
</gl-sprintf>
</details-row>
<details-row icon="information-o" padding="gl-p-4" data-testid="maven-group">
<gl-sprintf :message="$options.i18n.appGroup">
<template #group>
<strong>{{ packageEntity.maven_metadatum.app_group }}</strong>
</template>
</gl-sprintf>
</details-row>
</template>
</div>
</div>
</template>
......@@ -12,6 +12,7 @@ import {
GlTable,
GlSprintf,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import Tracking from '~/tracking';
import PackageActivity from './activity.vue';
import PackageHistory from './package_history.vue';
......@@ -25,6 +26,7 @@ import PypiInstallation from './pypi_installation.vue';
import PackagesListLoader from '../../shared/components/packages_list_loader.vue';
import PackageListRow from '../../shared/components/package_list_row.vue';
import DependencyRow from './dependency_row.vue';
import AdditionalMetadata from './additional_metadata.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import FileIcon from '~/vue_shared/components/file_icon.vue';
......@@ -32,7 +34,6 @@ import { generatePackageInfo } from '../utils';
import { __, s__ } from '~/locale';
import { PackageType, TrackingActions } from '../../shared/constants';
import { packageTypeToTrackCategory } from '../../shared/utils';
import { mapActions, mapState } from 'vuex';
export default {
name: 'PackagesApp',
......@@ -59,6 +60,7 @@ export default {
PackageListRow,
DependencyRow,
PackageHistory,
AdditionalMetadata,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -253,9 +255,12 @@ export default {
<package-activity />
</template>
<package-history v-else :package-entity="packageEntity" :project-name="projectName" />
<template v-else>
<package-history :package-entity="packageEntity" :project-name="projectName" />
<additional-metadata :package-entity="packageEntity" />
</template>
<h3 class="gl-font-lg">{{ __('Files') }}</h3>
<h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
<gl-table
:fields="$options.filesTableHeaderFields"
:items="filesTableRows"
......
......@@ -19,7 +19,7 @@ export default {
</script>
<template>
<timeline-entry-item class="system-note note-wrapper gl-my-6!">
<timeline-entry-item class="system-note note-wrapper gl-mb-6!">
<div class="timeline-icon">
<gl-icon :name="icon" />
</div>
......
......@@ -44,8 +44,8 @@ export default {
<template>
<div class="issuable-discussion">
<h3 class="gl-ml-6" data-testid="title">{{ __('History') }}</h3>
<ul class="timeline main-notes-list notes gl-my-4" data-testid="timeline">
<h3 class="gl-font-lg gl-my-3" data-testid="title">{{ __('History') }}</h3>
<ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
<history-element icon="clock" data-testid="created-on">
<gl-sprintf :message="$options.i18n.createdOn">
<template #name>
......
......@@ -7,7 +7,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
import DeleteButton from '../delete_button.vue';
import ListItem from '../list_item.vue';
import DetailsRow from './details_row.vue';
import DetailsRow from '~/registry/shared/components/details_row.vue';
import {
REMOVE_TAG_BUTTON_TITLE,
DIGEST_LABEL,
......
......@@ -10,13 +10,29 @@ export default {
type: String,
required: true,
},
padding: {
type: String,
default: 'gl-py-2',
required: false,
},
dashed: {
type: Boolean,
default: false,
required: false,
},
},
computed: {
borderClass() {
return this.dashed ? 'gl-border-b-solid gl-border-gray-100 gl-border-b-1' : '';
},
},
};
</script>
<template>
<div
class="gl-display-flex gl-align-items-center gl-font-monospace gl-font-sm gl-py-2 gl-word-break-all"
class="gl-display-flex gl-align-items-center gl-font-monospace gl-font-sm gl-word-break-all"
:class="[padding, borderClass]"
>
<gl-icon :name="icon" class="gl-mr-4" />
<span>
......
......@@ -1629,6 +1629,9 @@ msgstr ""
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
msgid "Additional Metadata"
msgstr ""
msgid "Additional minutes"
msgstr ""
......@@ -16914,6 +16917,12 @@ msgstr ""
msgid "PackageRegistry|Add NuGet Source"
msgstr ""
msgid "PackageRegistry|App group: %{group}"
msgstr ""
msgid "PackageRegistry|App name: %{name}"
msgstr ""
msgid "PackageRegistry|Commit %{link} on branch %{branch}"
msgstr ""
......@@ -17004,6 +17013,9 @@ msgstr ""
msgid "PackageRegistry|Learn how to %{noPackagesLinkStart}publish and share your packages%{noPackagesLinkEnd} with GitLab."
msgstr ""
msgid "PackageRegistry|License information located at %{link}"
msgstr ""
msgid "PackageRegistry|Manually Published"
msgstr ""
......@@ -17046,6 +17058,9 @@ msgstr ""
msgid "PackageRegistry|PyPi"
msgstr ""
msgid "PackageRegistry|Recipe: %{recipe}"
msgstr ""
msgid "PackageRegistry|Registry Setup"
msgstr ""
......@@ -17055,6 +17070,9 @@ msgstr ""
msgid "PackageRegistry|Sorry, your filter produced no results"
msgstr ""
msgid "PackageRegistry|Source project located at %{link}"
msgstr ""
msgid "PackageRegistry|There are no %{packageType} packages yet"
msgstr ""
......
......@@ -2,7 +2,7 @@
exports[`History Element renders the correct markup 1`] = `
<li
class="timeline-entry system-note note-wrapper gl-my-6!"
class="timeline-entry system-note note-wrapper gl-mb-6!"
>
<div
class="timeline-entry-inner"
......
import { shallowMount } from '@vue/test-utils';
import { GlLink, GlSprintf } from '@gitlab/ui';
import DetailsRow from '~/registry/shared/components/details_row.vue';
import component from '~/packages/details/components/additional_metadata.vue';
import { mavenPackage, conanPackage, nugetPackage, npmPackage } from '../../mock_data';
describe('Package Additional Metadata', () => {
let wrapper;
const defaultProps = {
packageEntity: { ...mavenPackage },
};
const mountComponent = props => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
stubs: {
DetailsRow,
GlSprintf,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findTitle = () => wrapper.find('[data-testid="title"]');
const findMainArea = () => wrapper.find('[data-testid="main"]');
const findNugetSource = () => wrapper.find('[data-testid="nuget-source"]');
const findNugetLicense = () => wrapper.find('[data-testid="nuget-license"]');
const findConanRecipe = () => wrapper.find('[data-testid="conan-recipe"]');
const findMavenApp = () => wrapper.find('[data-testid="maven-app"]');
const findMavenGroup = () => wrapper.find('[data-testid="maven-group"]');
const findElementLink = container => container.find(GlLink);
it('has the correct title', () => {
mountComponent();
const title = findTitle();
expect(title.exists()).toBe(true);
expect(title.text()).toBe('Additional Metadata');
});
describe.each`
packageEntity | visible | metadata
${mavenPackage} | ${true} | ${'maven_metadatum'}
${conanPackage} | ${true} | ${'conan_metadatum'}
${nugetPackage} | ${true} | ${'nuget_metadatum'}
${npmPackage} | ${false} | ${null}
`('Component visibility', ({ packageEntity, visible, metadata }) => {
it(`Is ${visible} that the component markup is visible when the package is ${packageEntity.package_type}`, () => {
mountComponent({ packageEntity });
expect(findTitle().exists()).toBe(visible);
expect(findMainArea().exists()).toBe(visible);
});
it(`The component is hidden if ${metadata} is missing`, () => {
mountComponent({ packageEntity: { ...packageEntity, [metadata]: null } });
expect(findTitle().exists()).toBe(false);
expect(findMainArea().exists()).toBe(false);
});
});
describe('nuget metadata', () => {
beforeEach(() => {
mountComponent({ packageEntity: nugetPackage });
});
it.each`
name | finderFunction | text | link | icon
${'source'} | ${findNugetSource} | ${'Source project located at project-foo-url'} | ${'project_url'} | ${'project'}
${'license'} | ${findNugetLicense} | ${'License information located at license-foo-url'} | ${'license_url'} | ${'license'}
`('$name element', ({ finderFunction, text, link, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
expect(findElementLink(element).attributes('href')).toBe(nugetPackage.nuget_metadatum[link]);
});
});
describe('conan metadata', () => {
beforeEach(() => {
mountComponent({ packageEntity: conanPackage });
});
it.each`
name | finderFunction | text | icon
${'recipe'} | ${findConanRecipe} | ${'Recipe: conan-package/1.0.0@conan+conan-package/stable'} | ${'information-o'}
`('$name element', ({ finderFunction, text, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
});
});
describe('maven metadata', () => {
beforeEach(() => {
mountComponent();
});
it.each`
name | finderFunction | text | icon
${'app'} | ${findMavenApp} | ${'App name: test-app'} | ${'information-o'}
${'group'} | ${findMavenGroup} | ${'App group: com.test.app'} | ${'information-o'}
`('$name element', ({ finderFunction, text, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
});
});
});
......@@ -17,6 +17,7 @@ import NugetInstallation from '~/packages/details/components/nuget_installation.
import PypiInstallation from '~/packages/details/components/pypi_installation.vue';
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 PackageActivity from '~/packages/details/components/activity.vue';
import {
conanPackage,
......@@ -99,6 +100,7 @@ describe('PackagesApp', () => {
const noDependenciesMessage = () => wrapper.find('[data-testid="no-dependencies-message"]');
const dependencyRows = () => wrapper.findAll(DependencyRow);
const findPackageHistory = () => wrapper.find(PackageHistory);
const findAdditionalMetadata = () => wrapper.find(AdditionalMetadata);
const findPackageActivity = () => wrapper.find(PackageActivity);
const findOldPackageInfo = () => wrapper.find('[data-testid="old-package-info"]');
......@@ -295,30 +297,38 @@ describe('PackagesApp', () => {
});
});
it('package history has the right props', () => {
createComponent({ oneColumnView: true });
expect(findPackageHistory().props('packageEntity')).toEqual(wrapper.vm.packageEntity);
expect(findPackageHistory().props('projectName')).toEqual(wrapper.vm.projectName);
});
it('additional metadata has the right props', () => {
createComponent({ oneColumnView: true });
expect(findAdditionalMetadata().props('packageEntity')).toEqual(wrapper.vm.packageEntity);
});
describe('one column layout feature flag', () => {
describe.each`
oneColumnView | history | oldInfo | activity
${true} | ${true} | ${false} | ${false}
${false} | ${false} | ${true} | ${true}
`(
'with oneColumnView set to $oneColumnView',
({ oneColumnView, history, oldInfo, activity }) => {
describe.each([true, false])('with oneColumnView set to %s', oneColumnView => {
beforeEach(() => {
createComponent({ oneColumnView });
});
it('package history', () => {
expect(findPackageHistory().exists()).toBe(history);
it(`is ${oneColumnView} that package history is visible`, () => {
expect(findPackageHistory().exists()).toBe(oneColumnView);
});
it('old info block', () => {
expect(findOldPackageInfo().exists()).toBe(oldInfo);
it(`is ${oneColumnView} that additional metadata is visible`, () => {
expect(findAdditionalMetadata().exists()).toBe(oneColumnView);
});
it('package activity', () => {
expect(findPackageActivity().exists()).toBe(activity);
it(`is ${!oneColumnView} that old info block is visible`, () => {
expect(findOldPackageInfo().exists()).toBe(!oneColumnView);
});
it(`is ${!oneColumnView} that package activity is visible`, () => {
expect(findPackageActivity().exists()).toBe(!oneColumnView);
});
});
},
);
});
});
......@@ -5,7 +5,7 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import component from '~/registry/explorer/components/details_page/tags_list_row.vue';
import DeleteButton from '~/registry/explorer/components/delete_button.vue';
import DetailsRow from '~/registry/explorer/components/details_page/details_row.vue';
import DetailsRow from '~/registry/shared/components/details_row.vue';
import {
REMOVE_TAG_BUTTON_TITLE,
REMOVE_TAG_BUTTON_DISABLE_TOOLTIP,
......
import { shallowMount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import component from '~/registry/explorer/components/details_page/details_row.vue';
import component from '~/registry/shared/components/details_row.vue';
describe('DetailsRow', () => {
let wrapper;
......@@ -8,10 +8,11 @@ describe('DetailsRow', () => {
const findIcon = () => wrapper.find(GlIcon);
const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
const mountComponent = () => {
const mountComponent = props => {
wrapper = shallowMount(component, {
propsData: {
icon: 'clock',
...props,
},
slots: {
default: '<div data-testid="default-slot"></div>',
......@@ -24,6 +25,12 @@ describe('DetailsRow', () => {
wrapper = null;
});
it('has a default slot', () => {
mountComponent();
expect(findDefaultSlot().exists()).toBe(true);
});
describe('icon prop', () => {
it('contains an icon', () => {
mountComponent();
expect(findIcon().exists()).toBe(true);
......@@ -35,9 +42,30 @@ describe('DetailsRow', () => {
name: 'clock',
});
});
});
it('has a default slot', () => {
describe('padding prop', () => {
it('padding has a default', () => {
mountComponent();
expect(findDefaultSlot().exists()).toBe(true);
expect(wrapper.classes('gl-py-2')).toBe(true);
});
it('is reflected in the template', () => {
mountComponent({ padding: 'gl-py-4' });
expect(wrapper.classes('gl-py-4')).toBe(true);
});
});
describe('dashed prop', () => {
const borderClasses = ['gl-border-b-solid', 'gl-border-gray-100', 'gl-border-b-1'];
it('by default component has no border', () => {
mountComponent();
expect(wrapper.classes).not.toEqual(expect.arrayContaining(borderClasses));
});
it('has a border when dashed is true', () => {
mountComponent({ dashed: true });
expect(wrapper.classes()).toEqual(expect.arrayContaining(borderClasses));
});
});
});
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