Commit 9f30c3e7 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Olena Horal-Koretska

Add new data points to graphql query

- query
- mock datra
parent 0f8007bd
<script> <script>
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { sprintf } from '~/locale'; import { sprintf, n__ } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
import { DETAILS_PAGE_TITLE, UPDATED_AT } from '../../constants/index'; import {
DETAILS_PAGE_TITLE,
UPDATED_AT,
CLEANUP_UNSCHEDULED_TEXT,
CLEANUP_SCHEDULED_TEXT,
CLEANUP_ONGOING_TEXT,
CLEANUP_UNFINISHED_TEXT,
CLEANUP_DISABLED_TEXT,
CLEANUP_SCHEDULED_TOOLTIP,
CLEANUP_ONGOING_TOOLTIP,
CLEANUP_UNFINISHED_TOOLTIP,
CLEANUP_DISABLED_TOOLTIP,
UNFINISHED_STATUS,
UNSCHEDULED_STATUS,
SCHEDULED_STATUS,
ONGOING_STATUS,
} from '../../constants/index';
export default { export default {
name: 'DetailsHeader', name: 'DetailsHeader',
...@@ -15,6 +31,11 @@ export default { ...@@ -15,6 +31,11 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
metadataLoading: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
visibilityIcon() { visibilityIcon() {
...@@ -26,6 +47,24 @@ export default { ...@@ -26,6 +47,24 @@ export default {
updatedText() { updatedText() {
return sprintf(UPDATED_AT, { time: this.timeAgo }); return sprintf(UPDATED_AT, { time: this.timeAgo });
}, },
tagCountText() {
return n__('%d tag', '%d tags', this.image.tagsCount);
},
cleanupTextAndTooltip() {
if (!this.image.project.containerExpirationPolicy?.enabled) {
return { text: CLEANUP_DISABLED_TEXT, tooltip: CLEANUP_DISABLED_TOOLTIP };
}
return {
[UNSCHEDULED_STATUS]: {
text: sprintf(CLEANUP_UNSCHEDULED_TEXT, {
time: this.timeFormatted(this.image.project.containerExpirationPolicy.nextRunAt),
}),
},
[SCHEDULED_STATUS]: { text: CLEANUP_SCHEDULED_TEXT, tooltip: CLEANUP_SCHEDULED_TOOLTIP },
[ONGOING_STATUS]: { text: CLEANUP_ONGOING_TEXT, tooltip: CLEANUP_ONGOING_TOOLTIP },
[UNFINISHED_STATUS]: { text: CLEANUP_UNFINISHED_TEXT, tooltip: CLEANUP_UNFINISHED_TOOLTIP },
}[this.image?.expirationPolicyCleanupStatus];
},
}, },
i18n: { i18n: {
DETAILS_PAGE_TITLE, DETAILS_PAGE_TITLE,
...@@ -34,7 +73,7 @@ export default { ...@@ -34,7 +73,7 @@ export default {
</script> </script>
<template> <template>
<title-area> <title-area :metadata-loading="metadataLoading">
<template #title> <template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE"> <gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName> <template #imageName>
...@@ -42,6 +81,20 @@ export default { ...@@ -42,6 +81,20 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</template> </template>
<template #metadata-tags-count>
<metadata-item icon="tag" :text="tagCountText" data-testid="tags-count" />
</template>
<template #metadata-cleanup>
<metadata-item
icon="expire"
:text="cleanupTextAndTooltip.text"
:text-tooltip="cleanupTextAndTooltip.tooltip"
size="xl"
data-testid="cleanup"
/>
</template>
<template #metadata-updated> <template #metadata-updated>
<metadata-item <metadata-item
:icon="visibilityIcon" :icon="visibilityIcon"
......
...@@ -60,6 +60,22 @@ export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}'); ...@@ -60,6 +60,22 @@ export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
export const NOT_AVAILABLE_TEXT = __('N/A'); export const NOT_AVAILABLE_TEXT = __('N/A');
export const NOT_AVAILABLE_SIZE = __('0 bytes'); export const NOT_AVAILABLE_SIZE = __('0 bytes');
export const CLEANUP_UNSCHEDULED_TEXT = s__('ContainerRegistry|Cleanup will run %{time}');
export const CLEANUP_SCHEDULED_TEXT = s__('ContainerRegistry|Cleanup pending');
export const CLEANUP_ONGOING_TEXT = s__('ContainerRegistry|Cleanup in progress');
export const CLEANUP_UNFINISHED_TEXT = s__('ContainerRegistry|Cleanup incomplete');
export const CLEANUP_DISABLED_TEXT = s__('ContainerRegistry|Cleanup disabled');
export const CLEANUP_SCHEDULED_TOOLTIP = s__('ContainerRegistry|Cleanup will run soon');
export const CLEANUP_ONGOING_TOOLTIP = s__('ContainerRegistry|Cleanup is currently removing tags');
export const CLEANUP_UNFINISHED_TOOLTIP = s__(
'ContainerRegistry|Cleanup ran but some tags were not removed',
);
export const CLEANUP_DISABLED_TOOLTIP = s__(
'ContainerRegistry|Cleanup is disabled for this project',
);
// Parameters // Parameters
export const DEFAULT_PAGE = 1; export const DEFAULT_PAGE = 1;
...@@ -76,3 +92,8 @@ export const ALERT_MESSAGES = { ...@@ -76,3 +92,8 @@ export const ALERT_MESSAGES = {
[ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE, [ALERT_SUCCESS_TAGS]: DELETE_TAGS_SUCCESS_MESSAGE,
[ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE, [ALERT_DANGER_TAGS]: DELETE_TAGS_ERROR_MESSAGE,
}; };
export const UNFINISHED_STATUS = 'UNFINISHED';
export const UNSCHEDULED_STATUS = 'UNSCHEDULED';
export const SCHEDULED_STATUS = 'SCHEDULED';
export const ONGOING_STATUS = 'ONGOING';
...@@ -18,6 +18,7 @@ query getContainerRepositoryDetails( ...@@ -18,6 +18,7 @@ query getContainerRepositoryDetails(
updatedAt updatedAt
tagsCount tagsCount
expirationPolicyStartedAt expirationPolicyStartedAt
expirationPolicyCleanupStatus
tags(after: $after, before: $before, first: $first, last: $last) { tags(after: $after, before: $before, first: $first, last: $last) {
nodes { nodes {
digest digest
...@@ -36,6 +37,10 @@ query getContainerRepositoryDetails( ...@@ -36,6 +37,10 @@ query getContainerRepositoryDetails(
} }
project { project {
visibility visibility
containerExpirationPolicy {
enabled
nextRunAt
}
} }
} }
} }
...@@ -22,6 +22,7 @@ import { ...@@ -22,6 +22,7 @@ import {
ALERT_DANGER_TAGS, ALERT_DANGER_TAGS,
GRAPHQL_PAGE_SIZE, GRAPHQL_PAGE_SIZE,
FETCH_IMAGES_LIST_ERROR_MESSAGE, FETCH_IMAGES_LIST_ERROR_MESSAGE,
UNFINISHED_STATUS,
} from '../constants/index'; } from '../constants/index';
export default { export default {
...@@ -84,7 +85,10 @@ export default { ...@@ -84,7 +85,10 @@ export default {
return this.image?.tags?.nodes || []; return this.image?.tags?.nodes || [];
}, },
showPartialCleanupWarning() { showPartialCleanupWarning() {
return this.image?.expirationPolicyStartedAt && !this.dismissPartialCleanupWarning; return (
this.image?.expirationPolicyCleanupStatus === UNFINISHED_STATUS &&
!this.dismissPartialCleanupWarning
);
}, },
tracking() { tracking() {
return { return {
...@@ -184,7 +188,7 @@ export default { ...@@ -184,7 +188,7 @@ export default {
@dismiss="dismissPartialCleanupWarning = true" @dismiss="dismissPartialCleanupWarning = true"
/> />
<details-header :image="image" /> <details-header :image="image" :metadata-loading="isLoading" />
<tags-loader v-if="isLoading" /> <tags-loader v-if="isLoading" />
<template v-else> <template v-else>
......
<script> <script>
import { GlIcon, GlLink } from '@gitlab/ui'; import { GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
export default { export default {
...@@ -9,6 +9,9 @@ export default { ...@@ -9,6 +9,9 @@ export default {
GlLink, GlLink,
TooltipOnTruncate, TooltipOnTruncate,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
icon: { icon: {
type: String, type: String,
...@@ -32,6 +35,11 @@ export default { ...@@ -32,6 +35,11 @@ export default {
return !value || ['xs', 's', 'm', 'l', 'xl'].includes(value); return !value || ['xs', 's', 'm', 'l', 'xl'].includes(value);
}, },
}, },
textTooltip: {
type: String,
required: false,
default: '',
},
}, },
computed: { computed: {
sizeClass() { sizeClass() {
...@@ -55,9 +63,12 @@ export default { ...@@ -55,9 +63,12 @@ export default {
class="gl-font-weight-bold gl-display-inline-flex" class="gl-font-weight-bold gl-display-inline-flex"
:class="sizeClass" :class="sizeClass"
> >
<tooltip-on-truncate :title="text" class="gl-text-truncate"> <tooltip-on-truncate v-if="!textTooltip" :title="text" class="gl-text-truncate">
{{ text }} {{ text }}
</tooltip-on-truncate> </tooltip-on-truncate>
<span v-else v-gl-tooltip="{ title: textTooltip }" data-testid="text-tooltip-container">
{{ text }}</span
>
</div> </div>
</div> </div>
</template> </template>
...@@ -50,7 +50,7 @@ export default { ...@@ -50,7 +50,7 @@ export default {
<template> <template>
<div class="gl-display-flex gl-flex-direction-column"> <div class="gl-display-flex gl-flex-direction-column">
<div class="gl-display-flex gl-justify-content-space-between gl-py-3"> <div class="gl-display-flex gl-justify-content-space-between gl-py-3">
<div class="gl-flex-direction-column"> <div class="gl-flex-direction-column gl-flex-grow-1">
<div class="gl-display-flex"> <div class="gl-display-flex">
<gl-avatar <gl-avatar
v-if="avatar" v-if="avatar"
...@@ -85,7 +85,7 @@ export default { ...@@ -85,7 +85,7 @@ export default {
</template> </template>
<template v-else> <template v-else>
<div class="gl-w-full"> <div class="gl-w-full">
<gl-skeleton-loader :width="200" :height="16" preserve-aspect-ratio="xMinYMax meet"> <gl-skeleton-loader :width="960" :height="16" preserve-aspect-ratio="xMinYMax meet">
<circle cx="6" cy="8" r="6" /> <circle cx="6" cy="8" r="6" />
<rect x="16" y="4" width="200" height="8" rx="4" /> <rect x="16" y="4" width="200" height="8" rx="4" />
</gl-skeleton-loader> </gl-skeleton-loader>
......
---
title: Add tags count and cleanup status to registry details
merge_request: 50756
author:
type: changed
...@@ -7471,15 +7471,42 @@ msgstr "" ...@@ -7471,15 +7471,42 @@ msgstr ""
msgid "ContainerRegistry|CLI Commands" msgid "ContainerRegistry|CLI Commands"
msgstr "" msgstr ""
msgid "ContainerRegistry|Cleanup disabled"
msgstr ""
msgid "ContainerRegistry|Cleanup in progress"
msgstr ""
msgid "ContainerRegistry|Cleanup incomplete"
msgstr ""
msgid "ContainerRegistry|Cleanup is currently removing tags"
msgstr ""
msgid "ContainerRegistry|Cleanup is disabled for this project"
msgstr ""
msgid "ContainerRegistry|Cleanup pending"
msgstr ""
msgid "ContainerRegistry|Cleanup policy for tags is disabled" msgid "ContainerRegistry|Cleanup policy for tags is disabled"
msgstr "" msgstr ""
msgid "ContainerRegistry|Cleanup policy successfully saved." msgid "ContainerRegistry|Cleanup policy successfully saved."
msgstr "" msgstr ""
msgid "ContainerRegistry|Cleanup ran but some tags were not removed"
msgstr ""
msgid "ContainerRegistry|Cleanup timed out before it could delete all tags" msgid "ContainerRegistry|Cleanup timed out before it could delete all tags"
msgstr "" msgstr ""
msgid "ContainerRegistry|Cleanup will run %{time}"
msgstr ""
msgid "ContainerRegistry|Cleanup will run soon"
msgstr ""
msgid "ContainerRegistry|Configuration digest: %{digest}" msgid "ContainerRegistry|Configuration digest: %{digest}"
msgstr "" msgstr ""
......
...@@ -9,7 +9,7 @@ exports[`PackageTitle renders with tags 1`] = ` ...@@ -9,7 +9,7 @@ exports[`PackageTitle renders with tags 1`] = `
class="gl-display-flex gl-justify-content-space-between gl-py-3" class="gl-display-flex gl-justify-content-space-between gl-py-3"
> >
<div <div
class="gl-flex-direction-column" class="gl-flex-direction-column gl-flex-grow-1"
> >
<div <div
class="gl-display-flex" class="gl-display-flex"
...@@ -54,6 +54,7 @@ exports[`PackageTitle renders with tags 1`] = ` ...@@ -54,6 +54,7 @@ exports[`PackageTitle renders with tags 1`] = `
link="" link=""
size="s" size="s"
text="maven" text="maven"
texttooltip=""
/> />
</div> </div>
<div <div
...@@ -65,6 +66,7 @@ exports[`PackageTitle renders with tags 1`] = ` ...@@ -65,6 +66,7 @@ exports[`PackageTitle renders with tags 1`] = `
link="" link=""
size="s" size="s"
text="300 bytes" text="300 bytes"
texttooltip=""
/> />
</div> </div>
<div <div
...@@ -95,7 +97,7 @@ exports[`PackageTitle renders without tags 1`] = ` ...@@ -95,7 +97,7 @@ exports[`PackageTitle renders without tags 1`] = `
class="gl-display-flex gl-justify-content-space-between gl-py-3" class="gl-display-flex gl-justify-content-space-between gl-py-3"
> >
<div <div
class="gl-flex-direction-column" class="gl-flex-direction-column gl-flex-grow-1"
> >
<div <div
class="gl-display-flex" class="gl-display-flex"
...@@ -140,6 +142,7 @@ exports[`PackageTitle renders without tags 1`] = ` ...@@ -140,6 +142,7 @@ exports[`PackageTitle renders without tags 1`] = `
link="" link=""
size="s" size="s"
text="maven" text="maven"
texttooltip=""
/> />
</div> </div>
<div <div
...@@ -151,6 +154,7 @@ exports[`PackageTitle renders without tags 1`] = ` ...@@ -151,6 +154,7 @@ exports[`PackageTitle renders without tags 1`] = `
link="" link=""
size="s" size="s"
text="300 bytes" text="300 bytes"
texttooltip=""
/> />
</div> </div>
</div> </div>
......
...@@ -3,7 +3,18 @@ import { GlSprintf } from '@gitlab/ui'; ...@@ -3,7 +3,18 @@ import { GlSprintf } from '@gitlab/ui';
import { useFakeDate } from 'helpers/fake_date'; import { useFakeDate } from 'helpers/fake_date';
import TitleArea from '~/vue_shared/components/registry/title_area.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import component from '~/registry/explorer/components/details_page/details_header.vue'; import component from '~/registry/explorer/components/details_page/details_header.vue';
import { DETAILS_PAGE_TITLE } from '~/registry/explorer/constants'; import {
DETAILS_PAGE_TITLE,
UNSCHEDULED_STATUS,
SCHEDULED_STATUS,
ONGOING_STATUS,
UNFINISHED_STATUS,
CLEANUP_DISABLED_TEXT,
CLEANUP_DISABLED_TOOLTIP,
CLEANUP_SCHEDULED_TOOLTIP,
CLEANUP_ONGOING_TOOLTIP,
CLEANUP_UNFINISHED_TOOLTIP,
} from '~/registry/explorer/constants';
describe('Details Header', () => { describe('Details Header', () => {
let wrapper; let wrapper;
...@@ -11,15 +22,22 @@ describe('Details Header', () => { ...@@ -11,15 +22,22 @@ describe('Details Header', () => {
const defaultImage = { const defaultImage = {
name: 'foo', name: 'foo',
updatedAt: '2020-11-03T13:29:21Z', updatedAt: '2020-11-03T13:29:21Z',
tagsCount: 10,
project: { project: {
visibility: 'public', visibility: 'public',
containerExpirationPolicy: {
enabled: false,
},
}, },
}; };
// set the date to Dec 4, 2020 // set the date to Dec 4, 2020
useFakeDate(2020, 11, 4); useFakeDate(2020, 11, 4);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findLastUpdatedAndVisibility = () => wrapper.find('[data-testid="updated-and-visibility"]'); const findLastUpdatedAndVisibility = () => findByTestId('updated-and-visibility');
const findTagsCount = () => findByTestId('tags-count');
const findCleanup = () => findByTestId('cleanup');
const waitForMetadataItems = async () => { const waitForMetadataItems = async () => {
// Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available // Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
...@@ -54,25 +72,96 @@ describe('Details Header', () => { ...@@ -54,25 +72,96 @@ describe('Details Header', () => {
expect(wrapper.text()).toContain('foo'); expect(wrapper.text()).toContain('foo');
}); });
it('has a metadata item with last updated text', async () => { describe('metadata items', () => {
mountComponent(); describe('tags count', () => {
await waitForMetadataItems(); it('when there is more than one tag has the correct text', async () => {
mountComponent();
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('text')).toBe('Last updated 1 month ago'); expect(findTagsCount().props('text')).toBe('10 tags');
}); });
it('when there is one tag has the correct text', async () => {
mountComponent({ ...defaultImage, tagsCount: 1 });
await waitForMetadataItems();
expect(findTagsCount().props('text')).toBe('1 tag');
});
it('has the correct icon', async () => {
mountComponent();
await waitForMetadataItems();
expect(findTagsCount().props('icon')).toBe('tag');
});
});
describe('visibility icon', () => { describe('cleanup metadata item', () => {
it('shows an eye when the project is public', async () => { it('has the correct icon', async () => {
mountComponent(); mountComponent();
await waitForMetadataItems(); await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye'); expect(findCleanup().props('icon')).toBe('expire');
});
it('when the expiration policy is disabled', async () => {
mountComponent();
await waitForMetadataItems();
expect(findCleanup().props()).toMatchObject({
text: CLEANUP_DISABLED_TEXT,
textTooltip: CLEANUP_DISABLED_TOOLTIP,
});
});
it.each`
status | text | tooltip
${UNSCHEDULED_STATUS} | ${'Cleanup will run in 1 month'} | ${''}
${SCHEDULED_STATUS} | ${'Cleanup pending'} | ${CLEANUP_SCHEDULED_TOOLTIP}
${ONGOING_STATUS} | ${'Cleanup in progress'} | ${CLEANUP_ONGOING_TOOLTIP}
${UNFINISHED_STATUS} | ${'Cleanup incomplete'} | ${CLEANUP_UNFINISHED_TOOLTIP}
`(
'when the status is $status the text is $text and the tooltip is $tooltip',
async ({ status, text, tooltip }) => {
mountComponent({
...defaultImage,
expirationPolicyCleanupStatus: status,
project: {
containerExpirationPolicy: { enabled: true, nextRunAt: '2021-01-03T14:29:21Z' },
},
});
await waitForMetadataItems();
expect(findCleanup().props()).toMatchObject({
text,
textTooltip: tooltip,
});
},
);
}); });
it('shows an eye slashed when the project is not public', async () => {
mountComponent({ ...defaultImage, project: { visibility: 'private' } });
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye-slash'); describe('visibility and updated at ', () => {
it('has last updated text', async () => {
mountComponent();
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('text')).toBe('Last updated 1 month ago');
});
describe('visibility icon', () => {
it('shows an eye when the project is public', async () => {
mountComponent();
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye');
});
it('shows an eye slashed when the project is not public', async () => {
mountComponent({ ...defaultImage, project: { visibility: 'private' } });
await waitForMetadataItems();
expect(findLastUpdatedAndVisibility().props('icon')).toBe('eye-slash');
});
});
}); });
}); });
}); });
...@@ -117,8 +117,13 @@ export const containerRepositoryMock = { ...@@ -117,8 +117,13 @@ export const containerRepositoryMock = {
updatedAt: '2020-11-03T13:29:21Z', updatedAt: '2020-11-03T13:29:21Z',
tagsCount: 13, tagsCount: 13,
expirationPolicyStartedAt: null, expirationPolicyStartedAt: null,
expirationPolicyCleanupStatus: 'UNSCHEDULED',
project: { project: {
visibility: 'public', visibility: 'public',
containerExpirationPolicy: {
enabled: false,
nextRunAt: '2020-11-27T08:59:27Z',
},
__typename: 'Project', __typename: 'Project',
}, },
}; };
......
...@@ -15,6 +15,8 @@ import EmptyTagsState from '~/registry/explorer/components/details_page/empty_ta ...@@ -15,6 +15,8 @@ import EmptyTagsState from '~/registry/explorer/components/details_page/empty_ta
import getContainerRepositoryDetailsQuery from '~/registry/explorer/graphql/queries/get_container_repository_details.query.graphql'; import getContainerRepositoryDetailsQuery from '~/registry/explorer/graphql/queries/get_container_repository_details.query.graphql';
import deleteContainerRepositoryTagsMutation from '~/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql'; import deleteContainerRepositoryTagsMutation from '~/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql';
import { UNFINISHED_STATUS } from '~/registry/explorer/constants/index';
import { import {
graphQLImageDetailsMock, graphQLImageDetailsMock,
graphQLImageDetailsEmptyTagsMock, graphQLImageDetailsEmptyTagsMock,
...@@ -353,10 +355,13 @@ describe('Details Page', () => { ...@@ -353,10 +355,13 @@ describe('Details Page', () => {
mountComponent(); mountComponent();
await waitForApolloRequestRender(); await waitForApolloRequestRender();
expect(findDetailsHeader().props('image')).toMatchObject({ expect(findDetailsHeader().props()).toMatchObject({
name: containerRepositoryMock.name, metadataLoading: false,
project: { image: {
visibility: containerRepositoryMock.project.visibility, name: containerRepositoryMock.name,
project: {
visibility: containerRepositoryMock.project.visibility,
},
}, },
}); });
}); });
...@@ -398,13 +403,13 @@ describe('Details Page', () => { ...@@ -398,13 +403,13 @@ describe('Details Page', () => {
cleanupPoliciesHelpPagePath: 'bar', cleanupPoliciesHelpPagePath: 'bar',
}; };
describe('when expiration_policy_started is not null', () => { describe(`when expirationPolicyCleanupStatus is ${UNFINISHED_STATUS}`, () => {
let resolver; let resolver;
beforeEach(() => { beforeEach(() => {
resolver = jest.fn().mockResolvedValue( resolver = jest.fn().mockResolvedValue(
graphQLImageDetailsMock({ graphQLImageDetailsMock({
expirationPolicyStartedAt: Date.now().toString(), expirationPolicyCleanupStatus: UNFINISHED_STATUS,
}), }),
); );
}); });
...@@ -439,7 +444,7 @@ describe('Details Page', () => { ...@@ -439,7 +444,7 @@ describe('Details Page', () => {
}); });
}); });
describe('when expiration_policy_started is null', () => { describe(`when expirationPolicyCleanupStatus is not ${UNFINISHED_STATUS}`, () => {
it('the component is hidden', async () => { it('the component is hidden', async () => {
mountComponent(); mountComponent();
await waitForApolloRequestRender(); await waitForApolloRequestRender();
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlIcon, GlLink } from '@gitlab/ui'; import { GlIcon, GlLink } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import component from '~/vue_shared/components/registry/metadata_item.vue'; import component from '~/vue_shared/components/registry/metadata_item.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
...@@ -12,6 +13,9 @@ describe('Metadata Item', () => { ...@@ -12,6 +13,9 @@ describe('Metadata Item', () => {
const mountComponent = (propsData = defaultProps) => { const mountComponent = (propsData = defaultProps) => {
wrapper = shallowMount(component, { wrapper = shallowMount(component, {
propsData, propsData,
directives: {
GlTooltip: createMockDirective(),
},
}); });
}; };
...@@ -24,6 +28,7 @@ describe('Metadata Item', () => { ...@@ -24,6 +28,7 @@ describe('Metadata Item', () => {
const findLink = (w = wrapper) => w.find(GlLink); const findLink = (w = wrapper) => w.find(GlLink);
const findText = () => wrapper.find('[data-testid="metadata-item-text"]'); const findText = () => wrapper.find('[data-testid="metadata-item-text"]');
const findTooltipOnTruncate = (w = wrapper) => w.find(TooltipOnTruncate); const findTooltipOnTruncate = (w = wrapper) => w.find(TooltipOnTruncate);
const findTextTooltip = () => wrapper.find('[data-testid="text-tooltip-container"]');
describe.each(['xs', 's', 'm', 'l', 'xl'])('size class', (size) => { describe.each(['xs', 's', 'm', 'l', 'xl'])('size class', (size) => {
const className = `mw-${size}`; const className = `mw-${size}`;
...@@ -55,6 +60,22 @@ describe('Metadata Item', () => { ...@@ -55,6 +60,22 @@ describe('Metadata Item', () => {
expect(tooltip.exists()).toBe(true); expect(tooltip.exists()).toBe(true);
expect(tooltip.attributes('title')).toBe(defaultProps.text); expect(tooltip.attributes('title')).toBe(defaultProps.text);
}); });
describe('with tooltip prop set to something', () => {
const textTooltip = 'foo';
it('hides tooltip_on_truncate', () => {
mountComponent({ ...defaultProps, textTooltip });
expect(findTooltipOnTruncate(findText()).exists()).toBe(false);
});
it('set the tooltip on the text', () => {
mountComponent({ ...defaultProps, textTooltip });
const tooltip = getBinding(findTextTooltip().element, 'gl-tooltip');
expect(tooltip.value.title).toBe(textTooltip);
});
});
}); });
describe('link', () => { describe('link', () => {
......
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