summaryrefslogtreecommitdiff
path: root/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js')
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js382
1 files changed, 382 insertions, 0 deletions
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
new file mode 100644
index 00000000000..00b1d03b7c2
--- /dev/null
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
@@ -0,0 +1,382 @@
+import { GlFormCheckbox, GlSprintf, GlIcon, GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+
+import component from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue';
+import {
+ REMOVE_TAG_BUTTON_TITLE,
+ MISSING_MANIFEST_WARNING_TOOLTIP,
+ NOT_AVAILABLE_TEXT,
+ NOT_AVAILABLE_SIZE,
+} from '~/packages_and_registries/container_registry/explorer/constants/index';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+
+import { tagsMock } from '../../mock_data';
+import { ListItem } from '../../stubs';
+
+describe('tags list row', () => {
+ let wrapper;
+ const [tag] = [...tagsMock];
+
+ const defaultProps = { tag, isMobile: false, index: 0 };
+
+ const findCheckbox = () => wrapper.findComponent(GlFormCheckbox);
+ const findName = () => wrapper.find('[data-testid="name"]');
+ const findSize = () => wrapper.find('[data-testid="size"]');
+ const findTime = () => wrapper.find('[data-testid="time"]');
+ const findShortRevision = () => wrapper.find('[data-testid="digest"]');
+ const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
+ const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
+ const findDetailsRows = () => wrapper.findAll(DetailsRow);
+ const findPublishedDateDetail = () => wrapper.find('[data-testid="published-date-detail"]');
+ const findManifestDetail = () => wrapper.find('[data-testid="manifest-detail"]');
+ const findConfigurationDetail = () => wrapper.find('[data-testid="configuration-detail"]');
+ const findWarningIcon = () => wrapper.findComponent(GlIcon);
+ const findAdditionalActionsMenu = () => wrapper.findComponent(GlDropdown);
+ const findDeleteButton = () => wrapper.findComponent(GlDropdownItem);
+
+ const mountComponent = (propsData = defaultProps) => {
+ wrapper = shallowMount(component, {
+ stubs: {
+ GlSprintf,
+ ListItem,
+ DetailsRow,
+ GlDropdown,
+ },
+ propsData,
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('checkbox', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findCheckbox().exists()).toBe(true);
+ });
+
+ it("does not exist when the row can't be deleted", () => {
+ const customTag = { ...tag, canDelete: false };
+
+ mountComponent({ ...defaultProps, tag: customTag });
+
+ expect(findCheckbox().exists()).toBe(false);
+ });
+
+ it.each`
+ digest | disabled
+ ${'foo'} | ${true}
+ ${null} | ${false}
+ ${null} | ${true}
+ ${'foo'} | ${true}
+ `('is disabled when the digest $digest and disabled is $disabled', ({ digest, disabled }) => {
+ mountComponent({ tag: { ...tag, digest }, disabled });
+
+ expect(findCheckbox().attributes('disabled')).toBe('true');
+ });
+
+ it('is wired to the selected prop', () => {
+ mountComponent({ ...defaultProps, selected: true });
+
+ expect(findCheckbox().attributes('checked')).toBe('true');
+ });
+
+ it('when changed emit a select event', () => {
+ mountComponent();
+
+ findCheckbox().vm.$emit('change');
+
+ expect(wrapper.emitted('select')).toEqual([[]]);
+ });
+ });
+
+ describe('tag name', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findName().exists()).toBe(true);
+ });
+
+ it('has the correct text', () => {
+ mountComponent();
+
+ expect(findName().text()).toBe(tag.name);
+ });
+
+ it('has a tooltip', () => {
+ mountComponent();
+
+ const tooltip = getBinding(findName().element, 'gl-tooltip');
+
+ expect(tooltip.value.title).toBe(tag.name);
+ });
+
+ it('on mobile has mw-s class', () => {
+ mountComponent({ ...defaultProps, isMobile: true });
+
+ expect(findName().classes('mw-s')).toBe(true);
+ });
+ });
+
+ describe('clipboard button', () => {
+ it('exist if tag.location exist', () => {
+ mountComponent();
+
+ expect(findClipboardButton().exists()).toBe(true);
+ });
+
+ it('is hidden if tag does not have a location', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, location: null } });
+
+ expect(findClipboardButton().exists()).toBe(false);
+ });
+
+ it('has the correct props/attributes', () => {
+ mountComponent();
+
+ expect(findClipboardButton().attributes()).toMatchObject({
+ text: tag.location,
+ title: tag.location,
+ });
+ });
+
+ it('is disabled when the component is disabled', () => {
+ mountComponent({ ...defaultProps, disabled: true });
+
+ expect(findClipboardButton().attributes('disabled')).toBe('true');
+ });
+ });
+
+ describe('warning icon', () => {
+ it('is normally hidden', () => {
+ mountComponent();
+
+ expect(findWarningIcon().exists()).toBe(false);
+ });
+
+ it('is shown when the tag is broken', () => {
+ mountComponent({ tag: { ...tag, digest: null } });
+
+ expect(findWarningIcon().exists()).toBe(true);
+ });
+
+ it('has an appropriate tooltip', () => {
+ mountComponent({ tag: { ...tag, digest: null } });
+
+ const tooltip = getBinding(findWarningIcon().element, 'gl-tooltip');
+ expect(tooltip.value.title).toBe(MISSING_MANIFEST_WARNING_TOOLTIP);
+ });
+ });
+
+ describe('size', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findSize().exists()).toBe(true);
+ });
+
+ it('contains the totalSize and layers', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: '1024', layers: 10 } });
+
+ expect(findSize().text()).toMatchInterpolatedText('1.00 KiB · 10 layers');
+ });
+
+ it('when totalSize is giantic', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: '1099511627776', layers: 2 } });
+
+ expect(findSize().text()).toMatchInterpolatedText('1024.00 GiB · 2 layers');
+ });
+
+ it('when totalSize is missing', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: '0', layers: 10 } });
+
+ expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 10 layers`);
+ });
+
+ it('when layers are missing', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: '1024' } });
+
+ expect(findSize().text()).toMatchInterpolatedText('1.00 KiB');
+ });
+
+ it('when there is 1 layer', () => {
+ mountComponent({ ...defaultProps, tag: { ...tag, totalSize: '0', layers: 1 } });
+
+ expect(findSize().text()).toMatchInterpolatedText(`${NOT_AVAILABLE_SIZE} · 1 layer`);
+ });
+ });
+
+ describe('time', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findTime().exists()).toBe(true);
+ });
+
+ it('has the correct text', () => {
+ mountComponent();
+
+ expect(findTime().text()).toBe('Published');
+ });
+
+ it('contains time_ago_tooltip component', () => {
+ mountComponent();
+
+ expect(findTimeAgoTooltip().exists()).toBe(true);
+ });
+
+ it('pass the correct props to time ago tooltip', () => {
+ mountComponent();
+
+ expect(findTimeAgoTooltip().attributes()).toMatchObject({ time: tag.createdAt });
+ });
+ });
+
+ describe('digest', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findShortRevision().exists()).toBe(true);
+ });
+
+ it('has the correct text', () => {
+ mountComponent();
+
+ expect(findShortRevision().text()).toMatchInterpolatedText('Digest: 2cf3d2f');
+ });
+
+ it(`displays ${NOT_AVAILABLE_TEXT} when digest is missing`, () => {
+ mountComponent({ tag: { ...tag, digest: null } });
+
+ expect(findShortRevision().text()).toMatchInterpolatedText(`Digest: ${NOT_AVAILABLE_TEXT}`);
+ });
+ });
+
+ describe('additional actions menu', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findAdditionalActionsMenu().exists()).toBe(true);
+ });
+
+ it('has the correct props', () => {
+ mountComponent();
+
+ expect(findAdditionalActionsMenu().props()).toMatchObject({
+ icon: 'ellipsis_v',
+ text: 'More actions',
+ textSrOnly: true,
+ category: 'tertiary',
+ right: true,
+ });
+ });
+
+ it.each`
+ canDelete | digest | disabled | buttonDisabled
+ ${true} | ${null} | ${true} | ${true}
+ ${false} | ${'foo'} | ${true} | ${true}
+ ${false} | ${null} | ${true} | ${true}
+ ${true} | ${'foo'} | ${true} | ${true}
+ ${true} | ${'foo'} | ${false} | ${false}
+ `(
+ 'is $visible that is visible when canDelete is $canDelete and digest is $digest and disabled is $disabled',
+ ({ canDelete, digest, disabled, buttonDisabled }) => {
+ mountComponent({ ...defaultProps, tag: { ...tag, canDelete, digest }, disabled });
+
+ expect(findAdditionalActionsMenu().props('disabled')).toBe(buttonDisabled);
+ expect(findAdditionalActionsMenu().classes('gl-opacity-0')).toBe(buttonDisabled);
+ expect(findAdditionalActionsMenu().classes('gl-pointer-events-none')).toBe(buttonDisabled);
+ },
+ );
+
+ describe('delete button', () => {
+ it('exists and has the correct attrs', () => {
+ mountComponent();
+
+ expect(findDeleteButton().exists()).toBe(true);
+ expect(findDeleteButton().attributes()).toMatchObject({
+ variant: 'danger',
+ });
+ expect(findDeleteButton().text()).toBe(REMOVE_TAG_BUTTON_TITLE);
+ });
+
+ it('delete event emits delete', () => {
+ mountComponent();
+
+ findDeleteButton().vm.$emit('click');
+
+ expect(wrapper.emitted('delete')).toEqual([[]]);
+ });
+ });
+ });
+
+ describe('details rows', () => {
+ describe('when the tag has a digest', () => {
+ it('has 3 details rows', async () => {
+ mountComponent();
+ await nextTick();
+
+ expect(findDetailsRows().length).toBe(3);
+ });
+
+ describe.each`
+ name | finderFunction | text | icon | clipboard
+ ${'published date detail'} | ${findPublishedDateDetail} | ${'Published to the gitlab-org/gitlab-test/rails-12009 image repository at 01:29 UTC on 2020-11-03'} | ${'clock'} | ${false}
+ ${'manifest detail'} | ${findManifestDetail} | ${'Manifest digest: sha256:2cf3d2fdac1b04a14301d47d51cb88dcd26714c74f91440eeee99ce399089062'} | ${'log'} | ${true}
+ ${'configuration detail'} | ${findConfigurationDetail} | ${'Configuration digest: sha256:c2613843ab33aabf847965442b13a8b55a56ae28837ce182627c0716eb08c02b'} | ${'cloud-gear'} | ${true}
+ `('$name details row', ({ finderFunction, text, icon, clipboard }) => {
+ it(`has ${text} as text`, async () => {
+ mountComponent();
+ await nextTick();
+
+ expect(finderFunction().text()).toMatchInterpolatedText(text);
+ });
+
+ it(`has the ${icon} icon`, async () => {
+ mountComponent();
+ await nextTick();
+
+ expect(finderFunction().props('icon')).toBe(icon);
+ });
+
+ if (clipboard) {
+ it(`clipboard button exist`, async () => {
+ mountComponent();
+ await nextTick();
+
+ expect(finderFunction().find(ClipboardButton).exists()).toBe(clipboard);
+ });
+
+ it('is disabled when the component is disabled', async () => {
+ mountComponent({ ...defaultProps, disabled: true });
+ await nextTick();
+
+ expect(finderFunction().findComponent(ClipboardButton).attributes('disabled')).toBe(
+ 'true',
+ );
+ });
+ }
+ });
+ });
+
+ describe('when the tag does not have a digest', () => {
+ it('hides the details rows', async () => {
+ mountComponent({ tag: { ...tag, digest: null } });
+
+ await nextTick();
+ expect(findDetailsRows().length).toBe(0);
+ });
+ });
+ });
+});