Commit c3ee0b83 authored by David O'Regan's avatar David O'Regan

Merge branch '216795-show-last-update-and-visibility' into 'master'

Add visibility and last updated image repository details

See merge request gitlab-org/gitlab!49703
parents d8723861 b15e1197
<script>
import { GlSprintf } from '@gitlab/ui';
import { sprintf } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { DETAILS_PAGE_TITLE } from '../../constants/index';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { DETAILS_PAGE_TITLE, UPDATED_AT } from '../../constants/index';
export default {
components: { GlSprintf, TitleArea },
components: { GlSprintf, TitleArea, MetadataItem },
mixins: [timeagoMixin],
props: {
imageName: {
type: String,
required: false,
default: '',
image: {
type: Object,
required: true,
},
},
computed: {
visibilityIcon() {
return this.image?.project?.visibility === 'public' ? 'eye' : 'eye-slash';
},
timeAgo() {
return this.timeFormatted(this.image.updatedAt);
},
updatedText() {
return sprintf(UPDATED_AT, { time: this.timeAgo });
},
},
i18n: {
......@@ -23,9 +37,17 @@ export default {
<template #title>
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
{{ imageName }}
{{ image.name }}
</template>
</gl-sprintf>
</template>
<template #metadata-updated>
<metadata-item
:icon="visibilityIcon"
:text="updatedText"
size="xl"
data-testid="updated-and-visibility"
/>
</template>
</title-area>
</template>
......@@ -56,6 +56,8 @@ export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
'ContainerRegistry|Invalid tag: missing manifest digest',
);
export const UPDATED_AT = s__('ContainerRegistry|Last updated %{time}');
export const NOT_AVAILABLE_TEXT = __('N/A');
export const NOT_AVAILABLE_SIZE = __('0 bytes');
// Parameters
......
......@@ -15,6 +15,7 @@ query getContainerRepositoryDetails(
location
canDelete
createdAt
updatedAt
tagsCount
expirationPolicyStartedAt
tags(after: $after, before: $before, first: $first, last: $last) {
......@@ -33,5 +34,8 @@ query getContainerRepositoryDetails(
...PageInfo
}
}
project {
visibility
}
}
}
......@@ -183,7 +183,7 @@ export default {
@dismiss="dismissPartialCleanupWarning = true"
/>
<details-header :image-name="image.name" />
<details-header :image="image" />
<tags-loader v-if="isLoading" />
<template v-else>
......
---
title: Add visibility and last updated image repository details
merge_request: 49703
author:
type: changed
......@@ -7450,6 +7450,9 @@ msgstr ""
msgid "ContainerRegistry|Keep these tags"
msgstr ""
msgid "ContainerRegistry|Last updated %{time}"
msgstr ""
msgid "ContainerRegistry|Login"
msgstr ""
......
......@@ -7,9 +7,27 @@ import { DETAILS_PAGE_TITLE } from '~/registry/explorer/constants';
describe('Details Header', () => {
let wrapper;
const mountComponent = propsData => {
const defaultImage = {
name: 'foo',
updatedAt: '2020-11-03T13:29:21Z',
project: {
visibility: 'public',
},
};
const findLastUpdatedAndVisibility = () => wrapper.find('[data-testid="updated-and-visibility"]');
const waitForMetadataItems = async () => {
// Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
await wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
};
const mountComponent = (image = defaultImage) => {
wrapper = shallowMount(component, {
propsData,
propsData: {
image,
},
stubs: {
GlSprintf,
TitleArea,
......@@ -23,12 +41,34 @@ describe('Details Header', () => {
});
it('has the correct title ', () => {
mountComponent();
mountComponent({ ...defaultImage, name: '' });
expect(wrapper.text()).toMatchInterpolatedText(DETAILS_PAGE_TITLE);
});
it('shows imageName in the title', () => {
mountComponent({ imageName: 'foo' });
mountComponent();
expect(wrapper.text()).toContain('foo');
});
it('has a metadata item with 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');
});
});
});
......@@ -114,8 +114,13 @@ export const containerRepositoryMock = {
location: 'host.docker.internal:5000/gitlab-org/gitlab-test/rails-12009',
canDelete: true,
createdAt: '2020-11-03T13:29:21Z',
updatedAt: '2020-11-03T13:29:21Z',
tagsCount: 13,
expirationPolicyStartedAt: null,
project: {
visibility: 'public',
__typename: 'Project',
},
};
export const tagsPageInfo = {
......
......@@ -353,7 +353,12 @@ describe('Details Page', () => {
mountComponent();
await waitForApolloRequestRender();
expect(findDetailsHeader().props()).toEqual({ imageName: containerRepositoryMock.name });
expect(findDetailsHeader().props('image')).toMatchObject({
name: containerRepositoryMock.name,
project: {
visibility: containerRepositoryMock.project.visibility,
},
});
});
});
......
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