Commit 2a33bcbc authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Kushal Pandya

Add new title_area component

- new component
- tests
parent a729fbbe
......@@ -147,10 +147,8 @@ export default {
/>
<div v-else class="packages-app">
<div class="detail-page-header d-flex justify-content-between flex-column flex-sm-row">
<package-title />
<div class="mt-sm-2">
<package-title>
<template #delete-button>
<gl-button
v-if="canDeletePackage"
v-gl-modal="'delete-modal'"
......@@ -161,8 +159,8 @@ export default {
>
{{ __('Delete') }}
</gl-button>
</div>
</div>
</template>
</package-title>
<gl-tabs>
<gl-tab :title="__('Detail')">
......
<script>
import { mapState, mapGetters } from 'vuex';
import { GlAvatar, GlIcon, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import { GlIcon, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import PackageTags from '../../shared/components/package_tags.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { __ } from '~/locale';
export default {
name: 'PackageTitle',
components: {
GlAvatar,
TitleArea,
GlIcon,
GlLink,
GlSprintf,
......@@ -36,77 +37,60 @@ export default {
</script>
<template>
<div class="gl-flex-direction-column">
<div class="gl-display-flex">
<gl-avatar
v-if="packageIcon"
:src="packageIcon"
shape="rect"
class="gl-align-self-center gl-mr-4"
data-testid="package-icon"
/>
<title-area :title="packageEntity.name" :avatar="packageIcon" data-qa-selector="package_title">
<template #sub-header>
<gl-icon name="eye" class="gl-mr-3" />
<gl-sprintf :message="$options.i18n.packageInfo">
<template #version>
{{ packageEntity.version }}
</template>
<div class="gl-display-flex gl-flex-direction-column">
<h1 class="gl-font-size-h1 gl-mt-3 gl-mb-2">
{{ packageEntity.name }}
</h1>
<template #timeAgo>
<span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)">
&nbsp;{{ timeFormatted(packageEntity.created_at) }}
</span>
</template>
</gl-sprintf>
</template>
<div class="gl-display-flex gl-align-items-center gl-text-gray-500">
<gl-icon name="eye" class="gl-mr-3" />
<gl-sprintf :message="$options.i18n.packageInfo">
<template #version>
{{ packageEntity.version }}
</template>
<template v-if="packageTypeDisplay" #metadata_type>
<gl-icon name="package" class="gl-text-gray-500 gl-mr-3" />
<span data-testid="package-type" class="gl-font-weight-bold">{{ packageTypeDisplay }}</span>
</template>
<template #timeAgo>
<span v-gl-tooltip :title="tooltipTitle(packageEntity.created_at)">
&nbsp;{{ timeFormatted(packageEntity.created_at) }}
</span>
</template>
</gl-sprintf>
</div>
</div>
</div>
<template #metadata_size>
<gl-icon name="disk" class="gl-text-gray-500 gl-mr-3" />
<span data-testid="package-size" class="gl-font-weight-bold">{{ totalSize }}</span>
</template>
<div class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mb-3">
<div v-if="packageTypeDisplay" class="gl-display-flex gl-align-items-center gl-mr-5">
<gl-icon name="package" class="gl-text-gray-500 gl-mr-3" />
<span data-testid="package-type" class="gl-font-weight-bold">{{ packageTypeDisplay }}</span>
</div>
<div class="gl-display-flex gl-align-items-center gl-mr-5">
<gl-icon name="disk" class="gl-text-gray-500 gl-mr-3" />
<span data-testid="package-size" class="gl-font-weight-bold">{{ totalSize }}</span>
</div>
<div v-if="packagePipeline" class="gl-display-flex gl-align-items-center gl-mr-5">
<gl-icon name="review-list" class="gl-text-gray-500 gl-mr-3" />
<gl-link
data-testid="pipeline-project"
:href="packagePipeline.project.web_url"
class="gl-font-weight-bold text-truncate"
>
{{ packagePipeline.project.name }}
</gl-link>
</div>
<template v-if="packagePipeline" #metadata_pipeline>
<gl-icon name="review-list" class="gl-text-gray-500 gl-mr-3" />
<gl-link
data-testid="pipeline-project"
:href="packagePipeline.project.web_url"
class="gl-font-weight-bold gl-str-truncated"
>
{{ packagePipeline.project.name }}
</gl-link>
</template>
<div
v-if="packagePipeline"
<template v-if="packagePipeline" #metadata_ref>
<gl-icon name="branch" data-testid="package-ref-icon" class="gl-text-gray-500 gl-mr-3" />
<span
v-gl-tooltip
data-testid="package-ref"
class="gl-display-flex gl-align-items-center gl-mr-5"
class="gl-font-weight-bold gl-str-truncated mw-xs"
:title="packagePipeline.ref"
>{{ packagePipeline.ref }}</span
>
<gl-icon name="branch" class="gl-text-gray-500 gl-mr-3" />
<span
v-gl-tooltip
class="gl-font-weight-bold text-truncate mw-xs"
:title="packagePipeline.ref"
>{{ packagePipeline.ref }}</span
>
</div>
</template>
<template v-if="hasTagsToDisplay" #metadata_tags>
<package-tags :tag-display-limit="2" :tags="packageEntity.tags" hide-label />
</template>
<div v-if="hasTagsToDisplay" class="gl-display-flex gl-align-items-center gl-mr-5">
<package-tags :tag-display-limit="2" :tags="packageEntity.tags" hide-label />
</div>
</div>
</div>
<template #right-actions>
<slot name="delete-button"></slot>
</template>
</title-area>
</template>
<script>
import { GlSprintf } from '@gitlab/ui';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { DETAILS_PAGE_TITLE } from '../../constants/index';
export default {
components: { GlSprintf },
components: { GlSprintf, TitleArea },
props: {
imageName: {
type: String,
......@@ -18,13 +19,13 @@ export default {
</script>
<template>
<div class="gl-display-flex gl-my-2 gl-align-items-center">
<h4>
<title-area>
<template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
{{ imageName }}
</template>
</gl-sprintf>
</h4>
</div>
</template>
</title-area>
</template>
<script>
import { GlSprintf, GlLink, GlIcon } from '@gitlab/ui';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { n__ } from '~/locale';
import { approximateDuration, calculateRemainingMilliseconds } from '~/lib/utils/datetime_utility';
......@@ -16,6 +17,7 @@ export default {
GlIcon,
GlSprintf,
GlLink,
TitleArea,
},
props: {
expirationPolicy: {
......@@ -85,37 +87,32 @@ export default {
<template>
<div>
<div
class="gl-display-flex gl-justify-content-space-between gl-align-items-center"
data-testid="header"
>
<h4 data-testid="title">{{ $options.i18n.CONTAINER_REGISTRY_TITLE }}</h4>
<div class="gl-display-none d-sm-block" data-testid="commands-slot">
<title-area :title="$options.i18n.CONTAINER_REGISTRY_TITLE">
<template #right-actions>
<slot name="commands"></slot>
</div>
</div>
<div
v-if="imagesCount"
class="gl-display-flex gl-align-items-center gl-mt-1 gl-mb-3 gl-text-gray-500"
data-testid="subheader"
>
<span class="gl-mr-3" data-testid="images-count">
<gl-icon class="gl-mr-1" name="container-image" />
<gl-sprintf :message="imagesCountText">
<template #count>
{{ imagesCount }}
</template>
</gl-sprintf>
</span>
<span v-if="!hideExpirationPolicyData" data-testid="expiration-policy">
<gl-icon class="gl-mr-1" name="expire" />
<gl-sprintf :message="expirationPolicyText">
<template #time>
{{ timeTillRun }}
</template>
</gl-sprintf>
</span>
</div>
</template>
<template #metadata_count>
<span v-if="imagesCount" data-testid="images-count">
<gl-icon class="gl-mr-1" name="container-image" />
<gl-sprintf :message="imagesCountText">
<template #count>
{{ imagesCount }}
</template>
</gl-sprintf>
</span>
</template>
<template #metadata_exp_policies>
<span v-if="!hideExpirationPolicyData" data-testid="expiration-policy">
<gl-icon class="gl-mr-1" name="expire" />
<gl-sprintf :message="expirationPolicyText">
<template #time>
{{ timeTillRun }}
</template>
</gl-sprintf>
</span>
</template>
</title-area>
<div data-testid="info-area">
<p>
<span data-testid="default-intro">
......
<script>
import { GlAvatar } from '@gitlab/ui';
export default {
name: 'TitleArea',
components: {
GlAvatar,
},
props: {
avatar: {
type: String,
default: null,
required: false,
},
title: {
type: String,
default: null,
required: false,
},
},
data() {
return {
metadataSlots: [],
};
},
mounted() {
this.metadataSlots = Object.keys(this.$slots).filter(k => k.startsWith('metadata_'));
},
};
</script>
<template>
<div class="gl-display-flex gl-justify-content-space-between gl-py-3">
<div class="gl-flex-direction-column">
<div class="gl-display-flex">
<gl-avatar v-if="avatar" :src="avatar" shape="rect" class="gl-align-self-center gl-mr-4" />
<div class="gl-display-flex gl-flex-direction-column">
<h1 class="gl-font-size-h1 gl-mt-3 gl-mb-2" data-testid="title">
<slot name="title">{{ title }}</slot>
</h1>
<div
v-if="$slots['sub-header']"
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1"
>
<slot name="sub-header"></slot>
</div>
</div>
</div>
<div class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3">
<div
v-for="(row, metadataIndex) in metadataSlots"
:key="metadataIndex"
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<slot :name="row"></slot>
</div>
</div>
</div>
<div v-if="$slots['right-actions']" class="gl-mt-3">
<slot name="right-actions"></slot>
</div>
</div>
</template>
......@@ -2,172 +2,171 @@
exports[`PackageTitle renders with tags 1`] = `
<div
class="gl-flex-direction-column"
class="gl-display-flex gl-justify-content-space-between gl-py-3"
data-qa-selector="package_title"
>
<div
class="gl-display-flex"
class="gl-flex-direction-column"
>
<!---->
<div
class="gl-display-flex"
>
<!---->
<div
class="gl-display-flex gl-flex-direction-column"
>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
data-testid="title"
>
Test package
</h1>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
/>
</div>
</div>
</div>
<div
class="gl-display-flex gl-flex-direction-column"
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"
>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
Test package
</h1>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="package"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-type"
>
maven
</span>
</div>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500"
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
class="gl-text-gray-500 gl-mr-3"
name="disk"
size="16"
/>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
<span
class="gl-font-weight-bold"
data-testid="package-size"
>
300 bytes
</span>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<package-tags-stub
hidelabel="true"
tagdisplaylimit="2"
tags="[object Object],[object Object],[object Object],[object Object]"
/>
</div>
</div>
</div>
<div
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mb-3"
>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="package"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-type"
>
maven
</span>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="disk"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-size"
>
300 bytes
</span>
</div>
<!---->
<!---->
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<package-tags-stub
hidelabel="true"
tagdisplaylimit="2"
tags="[object Object],[object Object],[object Object],[object Object]"
/>
</div>
</div>
<!---->
</div>
`;
exports[`PackageTitle renders without tags 1`] = `
<div
class="gl-flex-direction-column"
class="gl-display-flex gl-justify-content-space-between gl-py-3"
data-qa-selector="package_title"
>
<div
class="gl-display-flex"
class="gl-flex-direction-column"
>
<!---->
<div
class="gl-display-flex gl-flex-direction-column"
class="gl-display-flex"
>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
>
Test package
</h1>
<!---->
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500"
class="gl-display-flex gl-flex-direction-column"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
data-testid="title"
>
Test package
</h1>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
/>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
/>
</div>
</div>
</div>
</div>
<div
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mb-3"
>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="package"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-type"
>
maven
</span>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"
>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="disk"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-size"
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
300 bytes
</span>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="package"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-type"
>
maven
</span>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<gl-icon-stub
class="gl-text-gray-500 gl-mr-3"
name="disk"
size="16"
/>
<span
class="gl-font-weight-bold"
data-testid="package-size"
>
300 bytes
</span>
</div>
</div>
<!---->
<!---->
<!---->
</div>
<!---->
</div>
`;
......@@ -65,6 +65,8 @@ describe('PackagesApp', () => {
store,
stubs: {
...stubChildren(PackagesApp),
PackageTitle: false,
TitleArea: false,
GlButton: false,
GlModal: false,
GlTab: false,
......
......@@ -2,6 +2,7 @@ import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import PackageTitle from '~/packages/details/components/package_title.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import {
conanPackage,
mavenFiles,
......@@ -39,14 +40,19 @@ describe('PackageTitle', () => {
wrapper = shallowMount(PackageTitle, {
localVue,
store,
stubs: {
TitleArea,
},
});
return wrapper.vm.$nextTick();
}
const packageIcon = () => wrapper.find('[data-testid="package-icon"]');
const findTitleArea = () => wrapper.find(TitleArea);
const packageType = () => wrapper.find('[data-testid="package-type"]');
const packageSize = () => wrapper.find('[data-testid="package-size"]');
const pipelineProject = () => wrapper.find('[data-testid="pipeline-project"]');
const packageRef = () => wrapper.find('[data-testid="package-ref"]');
const packageRefIcon = () => wrapper.find('[data-testid="package-ref-icon"]');
const packageTags = () => wrapper.find(PackageTags);
afterEach(() => {
......@@ -54,38 +60,40 @@ describe('PackageTitle', () => {
});
describe('renders', () => {
it('without tags', () => {
createComponent();
it('without tags', async () => {
await createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('with tags', () => {
createComponent({ packageEntity: { ...mavenPackage, tags: mockTags } });
it('with tags', async () => {
await createComponent({ packageEntity: { ...mavenPackage, tags: mockTags } });
expect(wrapper.element).toMatchSnapshot();
});
});
describe('package icon', () => {
const fakeSrc = 'a-fake-src';
describe('package title', () => {
it('is correctly bound', async () => {
await createComponent();
it('shows an icon when provided one from vuex', () => {
createComponent({ icon: fakeSrc });
expect(packageIcon().exists()).toBe(true);
expect(findTitleArea().props('title')).toBe('Test package');
});
});
it('has the correct src attribute', () => {
createComponent({ icon: fakeSrc });
describe('package icon', () => {
const fakeSrc = 'a-fake-src';
expect(packageIcon().props('src')).toBe(fakeSrc);
it('binds an icon when provided one from vuex', async () => {
await createComponent({ icon: fakeSrc });
expect(findTitleArea().props('avatar')).toBe(fakeSrc);
});
it('does not show an icon when not provided one', () => {
createComponent();
it('do not binds an icon when not provided one', async () => {
await createComponent();
expect(packageIcon().exists()).toBe(false);
expect(findTitleArea().props('avatar')).toBe(null);
});
});
......@@ -104,22 +112,22 @@ describe('PackageTitle', () => {
});
describe('calculates the package size', () => {
it('correctly calulates when there is only 1 file', () => {
createComponent({ packageEntity: npmPackage, packageFiles: npmFiles });
it('correctly calculates when there is only 1 file', async () => {
await createComponent({ packageEntity: npmPackage, packageFiles: npmFiles });
expect(packageSize().text()).toBe('200 bytes');
});
it('correctly calulates when there are multiple files', () => {
createComponent();
it('correctly calulates when there are multiple files', async () => {
await createComponent();
expect(packageSize().text()).toBe('300 bytes');
});
});
describe('package tags', () => {
it('displays the package-tags component when the package has tags', () => {
createComponent({
it('displays the package-tags component when the package has tags', async () => {
await createComponent({
packageEntity: {
...npmPackage,
tags: mockTags,
......@@ -129,41 +137,36 @@ describe('PackageTitle', () => {
expect(packageTags().exists()).toBe(true);
});
it('does not display the package-tags component when there are no tags', () => {
createComponent();
it('does not display the package-tags component when there are no tags', async () => {
await createComponent();
expect(packageTags().exists()).toBe(false);
});
});
describe('package ref', () => {
it('does not display the ref if missing', () => {
createComponent();
it('does not display the ref if missing', async () => {
await createComponent();
expect(packageRef().exists()).toBe(false);
});
it('correctly shows the package ref if there is one', () => {
createComponent({ packageEntity: npmPackage });
expect(
packageRef()
.find('gl-icon-stub')
.exists(),
).toBe(true);
it('correctly shows the package ref if there is one', async () => {
await createComponent({ packageEntity: npmPackage });
expect(packageRefIcon().exists()).toBe(true);
expect(packageRef().text()).toBe(npmPackage.pipeline.ref);
});
});
describe('pipeline project', () => {
it('does not display the project if missing', () => {
createComponent();
it('does not display the project if missing', async () => {
await createComponent();
expect(pipelineProject().exists()).toBe(false);
});
it('correctly shows the pipeline project if there is one', () => {
createComponent({ packageEntity: npmPackage });
it('correctly shows the pipeline project if there is one', async () => {
await createComponent({ packageEntity: npmPackage });
expect(pipelineProject().text()).toBe(npmPackage.pipeline.project.name);
expect(pipelineProject().attributes('href')).toBe(npmPackage.pipeline.project.web_url);
......
import { shallowMount } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import component from '~/registry/explorer/components/details_page/details_header.vue';
import { DETAILS_PAGE_TITLE } from '~/registry/explorer/constants';
......@@ -11,6 +12,7 @@ describe('Details Header', () => {
propsData,
stubs: {
GlSprintf,
TitleArea,
},
});
};
......
import { shallowMount } from '@vue/test-utils';
import { GlSprintf, GlLink } from '@gitlab/ui';
import Component from '~/registry/explorer/components/list_page/registry_header.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import {
CONTAINER_REGISTRY_TITLE,
LIST_INTRO_TEXT,
......@@ -17,12 +18,10 @@ jest.mock('~/lib/utils/datetime_utility', () => ({
describe('registry_header', () => {
let wrapper;
const findHeader = () => wrapper.find('[data-testid="header"]');
const findTitle = () => wrapper.find('[data-testid="title"]');
const findTitleArea = () => wrapper.find(TitleArea);
const findCommandsSlot = () => wrapper.find('[data-testid="commands-slot"]');
const findInfoArea = () => wrapper.find('[data-testid="info-area"]');
const findIntroText = () => wrapper.find('[data-testid="default-intro"]');
const findSubHeader = () => wrapper.find('[data-testid="subheader"]');
const findImagesCountSubHeader = () => wrapper.find('[data-testid="images-count"]');
const findExpirationPolicySubHeader = () => wrapper.find('[data-testid="expiration-policy"]');
const findDisabledExpirationPolicyMessage = () =>
......@@ -32,10 +31,12 @@ describe('registry_header', () => {
wrapper = shallowMount(Component, {
stubs: {
GlSprintf,
TitleArea,
},
propsData,
slots,
});
return wrapper.vm.$nextTick();
};
afterEach(() => {
......@@ -44,90 +45,73 @@ describe('registry_header', () => {
});
describe('header', () => {
it('exists', () => {
it('has a title', () => {
mountComponent();
expect(findHeader().exists()).toBe(true);
});
it('contains the title of the page', () => {
mountComponent();
const title = findTitle();
expect(title.exists()).toBe(true);
expect(title.text()).toBe(CONTAINER_REGISTRY_TITLE);
expect(findTitleArea().props('title')).toBe(CONTAINER_REGISTRY_TITLE);
});
it('has a commands slot', () => {
mountComponent(null, { commands: 'baz' });
mountComponent(null, { commands: '<div data-testid="commands-slot">baz</div>' });
expect(findCommandsSlot().text()).toBe('baz');
});
});
describe('subheader', () => {
describe('when there are no images', () => {
it('is hidden ', () => {
mountComponent();
expect(findSubHeader().exists()).toBe(false);
});
});
describe('sub header parts', () => {
describe('images count', () => {
it('exists', async () => {
await mountComponent({ imagesCount: 1 });
describe('when there are images', () => {
it('is visible', () => {
mountComponent({ imagesCount: 1 });
expect(findSubHeader().exists()).toBe(true);
});
expect(findImagesCountSubHeader().exists()).toBe(true);
});
describe('sub header parts', () => {
describe('images count', () => {
it('exists', () => {
mountComponent({ imagesCount: 1 });
expect(findImagesCountSubHeader().exists()).toBe(true);
});
it('when there is one image', async () => {
await mountComponent({ imagesCount: 1 });
it('when there is one image', () => {
mountComponent({ imagesCount: 1 });
expect(findImagesCountSubHeader().text()).toMatchInterpolatedText('1 Image repository');
});
expect(findImagesCountSubHeader().text()).toMatchInterpolatedText('1 Image repository');
});
it('when there is more than one image', () => {
mountComponent({ imagesCount: 3 });
expect(findImagesCountSubHeader().text()).toMatchInterpolatedText(
'3 Image repositories',
);
});
it('when there is more than one image', async () => {
await mountComponent({ imagesCount: 3 });
expect(findImagesCountSubHeader().text()).toMatchInterpolatedText('3 Image repositories');
});
});
describe('expiration policy', () => {
it('when is disabled', () => {
mountComponent({
expirationPolicy: { enabled: false },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
});
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(true);
expect(text.text()).toMatchInterpolatedText(EXPIRATION_POLICY_DISABLED_TEXT);
describe('expiration policy', () => {
it('when is disabled', async () => {
await mountComponent({
expirationPolicy: { enabled: false },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
});
it('when is enabled', () => {
mountComponent({
expirationPolicy: { enabled: true },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
});
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(true);
expect(text.text()).toMatchInterpolatedText(EXPIRATION_POLICY_WILL_RUN_IN);
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(true);
expect(text.text()).toMatchInterpolatedText(EXPIRATION_POLICY_DISABLED_TEXT);
});
it('when is enabled', async () => {
await mountComponent({
expirationPolicy: { enabled: true },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
});
it('when the expiration policy is completely disabled', () => {
mountComponent({
expirationPolicy: { enabled: true },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
hideExpirationPolicyData: true,
});
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(false);
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(true);
expect(text.text()).toMatchInterpolatedText(EXPIRATION_POLICY_WILL_RUN_IN);
});
it('when the expiration policy is completely disabled', async () => {
await mountComponent({
expirationPolicy: { enabled: true },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
hideExpirationPolicyData: true,
});
const text = findExpirationPolicySubHeader();
expect(text.exists()).toBe(false);
});
});
});
......@@ -136,12 +120,13 @@ describe('registry_header', () => {
describe('info area', () => {
it('exists', () => {
mountComponent();
expect(findInfoArea().exists()).toBe(true);
});
describe('default message', () => {
beforeEach(() => {
mountComponent({ helpPagePath: 'bar' });
return mountComponent({ helpPagePath: 'bar' });
});
it('exists', () => {
......@@ -165,6 +150,7 @@ describe('registry_header', () => {
describe('when there are no images', () => {
it('is hidden', () => {
mountComponent();
expect(findDisabledExpirationPolicyMessage().exists()).toBe(false);
});
});
......@@ -172,7 +158,7 @@ describe('registry_header', () => {
describe('when there are images', () => {
describe('when expiration policy is disabled', () => {
beforeEach(() => {
mountComponent({
return mountComponent({
expirationPolicy: { enabled: false },
expirationPolicyHelpPagePath: 'foo',
imagesCount: 1,
......@@ -202,6 +188,7 @@ describe('registry_header', () => {
expirationPolicy: { enabled: true },
imagesCount: 1,
});
expect(findDisabledExpirationPolicyMessage().exists()).toBe(false);
});
});
......@@ -212,6 +199,7 @@ describe('registry_header', () => {
imagesCount: 1,
hideExpirationPolicyData: true,
});
expect(findDisabledExpirationPolicyMessage().exists()).toBe(false);
});
});
......
......@@ -8,6 +8,7 @@ import GroupEmptyState from '~/registry/explorer/components/list_page/group_empt
import ProjectEmptyState from '~/registry/explorer/components/list_page/project_empty_state.vue';
import RegistryHeader from '~/registry/explorer/components/list_page/registry_header.vue';
import ImageList from '~/registry/explorer/components/list_page/image_list.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { createStore } from '~/registry/explorer/stores/';
import {
SET_MAIN_LOADING,
......@@ -54,6 +55,7 @@ describe('List Page', () => {
GlEmptyState,
GlSprintf,
RegistryHeader,
TitleArea,
},
mocks: {
$toast,
......
import { GlAvatar } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import component from '~/vue_shared/components/registry/title_area.vue';
describe('title area', () => {
let wrapper;
const findSubHeaderSlot = () => wrapper.find('[data-testid="sub-header"]');
const findRightActionsSlot = () => wrapper.find('[data-testid="right-actions"]');
const findMetadataSlot = name => wrapper.find(`[data-testid="${name}"]`);
const findTitle = () => wrapper.find('[data-testid="title"]');
const findAvatar = () => wrapper.find(GlAvatar);
const mountComponent = ({ propsData = { title: 'foo' }, slots } = {}) => {
wrapper = shallowMount(component, {
propsData,
slots: {
'sub-header': '<div data-testid="sub-header" />',
'right-actions': '<div data-testid="right-actions" />',
...slots,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('title', () => {
it('if slot is not present defaults to prop', () => {
mountComponent();
expect(findTitle().text()).toBe('foo');
});
it('if slot is present uses slot', () => {
mountComponent({
slots: {
title: 'slot_title',
},
});
expect(findTitle().text()).toBe('slot_title');
});
});
describe('avatar', () => {
it('is shown if avatar props exist', () => {
mountComponent({ propsData: { title: 'foo', avatar: 'baz' } });
expect(findAvatar().props('src')).toBe('baz');
});
it('is hidden if avatar props does not exist', () => {
mountComponent();
expect(findAvatar().exists()).toBe(false);
});
});
describe.each`
slotName | finderFunction
${'sub-header'} | ${findSubHeaderSlot}
${'right-actions'} | ${findRightActionsSlot}
`('$slotName slot', ({ finderFunction, slotName }) => {
it('exist when the slot is filled', () => {
mountComponent();
expect(finderFunction().exists()).toBe(true);
});
it('does not exist when the slot is empty', () => {
mountComponent({ slots: { [slotName]: '' } });
expect(finderFunction().exists()).toBe(false);
});
});
describe.each`
slotNames
${['metadata_foo']}
${['metadata_foo', 'metadata_bar']}
${['metadata_foo', 'metadata_bar', 'metadata_baz']}
`('$slotNames metadata slots', ({ slotNames }) => {
const slotMocks = slotNames.reduce((acc, current) => {
acc[current] = `<div data-testid="${current}" />`;
return acc;
}, {});
it('exist when the slot is present', async () => {
mountComponent({ slots: slotMocks });
await wrapper.vm.$nextTick();
slotNames.forEach(name => {
expect(findMetadataSlot(name).exists()).toBe(true);
});
});
});
});
......@@ -32,7 +32,7 @@ RSpec.shared_examples 'package details link' do |property|
expect(page).to have_current_path(project_package_path(package.project, package))
page.within('.detail-page-header') do
page.within('[data-qa-selector="package_title"]') do
expect(page).to have_content(package.name)
end
......
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