summaryrefslogtreecommitdiff
path: root/spec/frontend/packages_and_registries/container_registry/explorer/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/packages_and_registries/container_registry/explorer/components')
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/__snapshots__/registry_breadcrumb_spec.js.snap84
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js76
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/empty_state_spec.js54
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js21
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js100
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/registry_breadcrumb_spec.js78
6 files changed, 126 insertions, 287 deletions
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/__snapshots__/registry_breadcrumb_spec.js.snap b/spec/frontend/packages_and_registries/container_registry/explorer/components/__snapshots__/registry_breadcrumb_spec.js.snap
deleted file mode 100644
index 7044c1285d8..00000000000
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/__snapshots__/registry_breadcrumb_spec.js.snap
+++ /dev/null
@@ -1,84 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Registry Breadcrumb when is not rootRoute renders 1`] = `
-<div
- class="gl-breadcrumbs"
->
-
- <ol
- class="breadcrumb gl-breadcrumb-list"
- >
- <li
- class="breadcrumb-item gl-breadcrumb-item"
- >
- <a
- class=""
- href="/"
- target="_self"
- >
- <span>
-
- </span>
-
- <span
- class="gl-breadcrumb-separator"
- data-testid="separator"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s8"
- data-testid="angle-right-icon"
- role="img"
- >
- <use
- href="#angle-right"
- />
- </svg>
- </span>
- </a>
- </li>
- <li
- class="breadcrumb-item gl-breadcrumb-item"
- >
- <a
- class=""
- href="#"
- target="_self"
- >
- <span>
-
- </span>
-
- <!---->
- </a>
- </li>
- </ol>
-</div>
-`;
-
-exports[`Registry Breadcrumb when is rootRoute renders 1`] = `
-<div
- class="gl-breadcrumbs"
->
-
- <ol
- class="breadcrumb gl-breadcrumb-list"
- >
- <li
- class="breadcrumb-item gl-breadcrumb-item"
- >
- <a
- class=""
- href="/"
- target="_self"
- >
- <span>
-
- </span>
-
- <!---->
- </a>
- </li>
- </ol>
-</div>
-`;
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
index f06300efa29..5278e730ec9 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
@@ -1,7 +1,6 @@
-import { GlDropdownItem, GlIcon } from '@gitlab/ui';
+import { GlDropdownItem, GlIcon, GlDropdown } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
-import { GlDropdown } from 'jest/packages_and_registries/container_registry/explorer/stubs';
import { useFakeDate } from 'helpers/fake_date';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
@@ -51,6 +50,7 @@ describe('Details Header', () => {
const findCleanup = () => findByTestId('cleanup');
const findDeleteButton = () => wrapper.findComponent(GlDropdownItem);
const findInfoIcon = () => wrapper.findComponent(GlIcon);
+ const findMenu = () => wrapper.findComponent(GlDropdown);
const waitForMetadataItems = async () => {
// Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
@@ -139,51 +139,53 @@ describe('Details Header', () => {
});
});
- describe('delete button', () => {
- it('exists', () => {
- mountComponent();
+ describe('menu', () => {
+ it.each`
+ canDelete | disabled | isVisible
+ ${true} | ${false} | ${true}
+ ${true} | ${true} | ${false}
+ ${false} | ${false} | ${false}
+ ${false} | ${true} | ${false}
+ `(
+ 'when canDelete is $canDelete and disabled is $disabled is $isVisible that the menu is visible',
+ ({ canDelete, disabled, isVisible }) => {
+ mountComponent({ propsData: { image: { ...defaultImage, canDelete }, disabled } });
- expect(findDeleteButton().exists()).toBe(true);
- });
+ expect(findMenu().exists()).toBe(isVisible);
+ },
+ );
- it('has the correct text', () => {
- mountComponent();
+ describe('delete button', () => {
+ it('exists', () => {
+ mountComponent();
- expect(findDeleteButton().text()).toBe('Delete image repository');
- });
+ expect(findDeleteButton().exists()).toBe(true);
+ });
- it('has the correct props', () => {
- mountComponent();
+ it('has the correct text', () => {
+ mountComponent();
- expect(findDeleteButton().attributes()).toMatchObject(
- expect.objectContaining({
- variant: 'danger',
- }),
- );
- });
+ expect(findDeleteButton().text()).toBe('Delete image repository');
+ });
- it('emits the correct event', () => {
- mountComponent();
+ it('has the correct props', () => {
+ mountComponent();
- findDeleteButton().vm.$emit('click');
+ expect(findDeleteButton().attributes()).toMatchObject(
+ expect.objectContaining({
+ variant: 'danger',
+ }),
+ );
+ });
- expect(wrapper.emitted('delete')).toEqual([[]]);
- });
+ it('emits the correct event', () => {
+ mountComponent();
- it.each`
- canDelete | disabled | isDisabled
- ${true} | ${false} | ${undefined}
- ${true} | ${true} | ${'true'}
- ${false} | ${false} | ${'true'}
- ${false} | ${true} | ${'true'}
- `(
- 'when canDelete is $canDelete and disabled is $disabled is $isDisabled that the button is disabled',
- ({ canDelete, disabled, isDisabled }) => {
- mountComponent({ propsData: { image: { ...defaultImage, canDelete }, disabled } });
+ findDeleteButton().vm.$emit('click');
- expect(findDeleteButton().attributes('disabled')).toBe(isDisabled);
- },
- );
+ expect(wrapper.emitted('delete')).toEqual([[]]);
+ });
+ });
});
describe('metadata items', () => {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/empty_state_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/empty_state_spec.js
deleted file mode 100644
index f14284e9efe..00000000000
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/empty_state_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { GlEmptyState } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import component from '~/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue';
-import {
- NO_TAGS_TITLE,
- NO_TAGS_MESSAGE,
- MISSING_OR_DELETED_IMAGE_TITLE,
- MISSING_OR_DELETED_IMAGE_MESSAGE,
-} from '~/packages_and_registries/container_registry/explorer/constants';
-
-describe('EmptyTagsState component', () => {
- let wrapper;
-
- const findEmptyState = () => wrapper.find(GlEmptyState);
-
- const mountComponent = (propsData) => {
- wrapper = shallowMount(component, {
- stubs: {
- GlEmptyState,
- },
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('contains gl-empty-state', () => {
- mountComponent();
- expect(findEmptyState().exists()).toBe(true);
- });
-
- it.each`
- isEmptyImage | title | description
- ${false} | ${NO_TAGS_TITLE} | ${NO_TAGS_MESSAGE}
- ${true} | ${MISSING_OR_DELETED_IMAGE_TITLE} | ${MISSING_OR_DELETED_IMAGE_MESSAGE}
- `(
- 'when isEmptyImage is $isEmptyImage has the correct props',
- ({ isEmptyImage, title, description }) => {
- mountComponent({
- noContainersImage: 'foo',
- isEmptyImage,
- });
-
- expect(findEmptyState().props()).toMatchObject({
- title,
- description,
- svgPath: 'foo',
- });
- },
- );
-});
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
index 00b1d03b7c2..057312828ff 100644
--- 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
@@ -75,16 +75,19 @@ describe('tags list row', () => {
});
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 });
+ digest | disabled | isDisabled
+ ${'foo'} | ${true} | ${'true'}
+ ${null} | ${true} | ${'true'}
+ ${null} | ${false} | ${undefined}
+ ${'foo'} | ${false} | ${undefined}
+ `(
+ 'disabled attribute is set to $isDisabled when the digest $digest and disabled is $disabled',
+ ({ digest, disabled, isDisabled }) => {
+ mountComponent({ tag: { ...tag, digest }, disabled });
- expect(findCheckbox().attributes('disabled')).toBe('true');
- });
+ expect(findCheckbox().attributes('disabled')).toBe(isDisabled);
+ },
+ );
it('is wired to the selected prop', () => {
mountComponent({ ...defaultProps, selected: true });
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
index 56f12e2f0bb..0dcf988c814 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_spec.js
@@ -1,16 +1,25 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { GlEmptyState } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { stripTypenames } from 'helpers/graphql_helpers';
-import EmptyTagsState from '~/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue';
+
import component from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue';
import TagsListRow from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row.vue';
import TagsLoader from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_loader.vue';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
+import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import getContainerRepositoryTagsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql';
-import { GRAPHQL_PAGE_SIZE } from '~/packages_and_registries/container_registry/explorer/constants/index';
+import {
+ GRAPHQL_PAGE_SIZE,
+ NO_TAGS_TITLE,
+ NO_TAGS_MESSAGE,
+ NO_TAGS_MATCHING_FILTERS_TITLE,
+ NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
+} from '~/packages_and_registries/container_registry/explorer/constants/index';
+import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { tagsMock, imageTagsMock, tagsPageInfo } from '../../mock_data';
const localVue = createLocalVue();
@@ -21,11 +30,20 @@ describe('Tags List', () => {
let resolver;
const tags = [...tagsMock];
+ const defaultConfig = {
+ noContainersImage: 'noContainersImage',
+ };
+
+ const findPersistedSearch = () => wrapper.findComponent(PersistedSearch);
const findTagsListRow = () => wrapper.findAllComponents(TagsListRow);
const findRegistryList = () => wrapper.findComponent(RegistryList);
- const findEmptyState = () => wrapper.findComponent(EmptyTagsState);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findTagsLoader = () => wrapper.findComponent(TagsLoader);
+ const fireFirstSortUpdate = () => {
+ findPersistedSearch().vm.$emit('update', { sort: 'NAME_ASC', filters: [] });
+ };
+
const waitForApolloRequestRender = async () => {
await waitForPromises();
await nextTick();
@@ -44,7 +62,7 @@ describe('Tags List', () => {
stubs: { RegistryList },
provide() {
return {
- config: {},
+ config: defaultConfig,
};
},
});
@@ -61,10 +79,23 @@ describe('Tags List', () => {
describe('registry list', () => {
beforeEach(() => {
mountComponent();
-
+ fireFirstSortUpdate();
return waitForApolloRequestRender();
});
+ it('has a persisted search', () => {
+ expect(findPersistedSearch().props()).toMatchObject({
+ defaultOrder: 'NAME',
+ defaultSort: 'asc',
+ sortableFields: [
+ {
+ label: 'Name',
+ orderBy: 'NAME',
+ },
+ ],
+ });
+ });
+
it('binds the correct props', () => {
expect(findRegistryList().props()).toMatchObject({
title: '2 tags',
@@ -75,11 +106,13 @@ describe('Tags List', () => {
});
describe('events', () => {
- it('prev-page fetch the previous page', () => {
+ it('prev-page fetch the previous page', async () => {
findRegistryList().vm.$emit('prev-page');
expect(resolver).toHaveBeenCalledWith({
first: null,
+ name: '',
+ sort: 'NAME_ASC',
before: tagsPageInfo.startCursor,
last: GRAPHQL_PAGE_SIZE,
id: '1',
@@ -92,6 +125,8 @@ describe('Tags List', () => {
expect(resolver).toHaveBeenCalledWith({
after: tagsPageInfo.endCursor,
first: GRAPHQL_PAGE_SIZE,
+ name: '',
+ sort: 'NAME_ASC',
id: '1',
});
});
@@ -108,6 +143,7 @@ describe('Tags List', () => {
describe('list rows', () => {
it('one row exist for each tag', async () => {
mountComponent();
+ fireFirstSortUpdate();
await waitForApolloRequestRender();
@@ -116,6 +152,7 @@ describe('Tags List', () => {
it('the correct props are bound to it', async () => {
mountComponent({ propsData: { disabled: true, id: 1 } });
+ fireFirstSortUpdate();
await waitForApolloRequestRender();
@@ -130,7 +167,7 @@ describe('Tags List', () => {
describe('events', () => {
it('select event update the selected items', async () => {
mountComponent();
-
+ fireFirstSortUpdate();
await waitForApolloRequestRender();
findTagsListRow().at(0).vm.$emit('select');
@@ -142,7 +179,7 @@ describe('Tags List', () => {
it('delete event emit a delete event', async () => {
mountComponent();
-
+ fireFirstSortUpdate();
await waitForApolloRequestRender();
findTagsListRow().at(0).vm.$emit('delete');
@@ -154,32 +191,45 @@ describe('Tags List', () => {
describe('when the list of tags is empty', () => {
beforeEach(() => {
resolver = jest.fn().mockResolvedValue(imageTagsMock([]));
- });
-
- it('has the empty state', async () => {
mountComponent();
-
- await waitForApolloRequestRender();
-
- expect(findEmptyState().exists()).toBe(true);
+ fireFirstSortUpdate();
+ return waitForApolloRequestRender();
});
- it('does not show the loader', async () => {
- mountComponent();
-
- await waitForApolloRequestRender();
-
+ it('does not show the loader', () => {
expect(findTagsLoader().exists()).toBe(false);
});
- it('does not show the list', async () => {
- mountComponent();
+ it('does not show the list', () => {
+ expect(findRegistryList().exists()).toBe(false);
+ });
- await waitForApolloRequestRender();
+ describe('empty state', () => {
+ it('default empty state', () => {
+ expect(findEmptyState().props()).toMatchObject({
+ svgPath: defaultConfig.noContainersImage,
+ title: NO_TAGS_TITLE,
+ description: NO_TAGS_MESSAGE,
+ });
+ });
- expect(findRegistryList().exists()).toBe(false);
+ it('when filtered shows a filtered message', async () => {
+ findPersistedSearch().vm.$emit('update', {
+ sort: 'NAME_ASC',
+ filters: [{ type: FILTERED_SEARCH_TERM, value: { data: 'foo' } }],
+ });
+
+ await waitForApolloRequestRender();
+
+ expect(findEmptyState().props()).toMatchObject({
+ svgPath: defaultConfig.noContainersImage,
+ title: NO_TAGS_MATCHING_FILTERS_TITLE,
+ description: NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
+ });
+ });
});
});
+
describe('loading state', () => {
it.each`
isImageLoading | queryExecuting | loadingVisible
@@ -191,7 +241,7 @@ describe('Tags List', () => {
'when the isImageLoading is $isImageLoading, and is $queryExecuting that the query is still executing is $loadingVisible that the loader is shown',
async ({ isImageLoading, queryExecuting, loadingVisible }) => {
mountComponent({ propsData: { isImageLoading, isMobile: false, id: 1 } });
-
+ fireFirstSortUpdate();
if (!queryExecuting) {
await waitForApolloRequestRender();
}
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/registry_breadcrumb_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/registry_breadcrumb_spec.js
deleted file mode 100644
index e5a8438f23f..00000000000
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/registry_breadcrumb_spec.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { mount } from '@vue/test-utils';
-
-import component from '~/packages_and_registries/container_registry/explorer/components/registry_breadcrumb.vue';
-
-describe('Registry Breadcrumb', () => {
- let wrapper;
- const nameGenerator = jest.fn();
-
- const routes = [
- { name: 'list', path: '/', meta: { nameGenerator, root: true } },
- { name: 'details', path: '/:id', meta: { nameGenerator } },
- ];
-
- const mountComponent = ($route) => {
- wrapper = mount(component, {
- mocks: {
- $route,
- $router: {
- options: {
- routes,
- },
- },
- },
- });
- };
-
- beforeEach(() => {
- nameGenerator.mockClear();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('when is rootRoute', () => {
- beforeEach(() => {
- mountComponent(routes[0]);
- });
-
- it('renders', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('contains only a single router-link to list', () => {
- const links = wrapper.findAll('a');
-
- expect(links).toHaveLength(1);
- expect(links.at(0).attributes('href')).toBe('/');
- });
-
- it('the link text is calculated by nameGenerator', () => {
- expect(nameGenerator).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when is not rootRoute', () => {
- beforeEach(() => {
- mountComponent(routes[1]);
- });
-
- it('renders', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('contains two router-links to list and details', () => {
- const links = wrapper.findAll('a');
-
- expect(links).toHaveLength(2);
- expect(links.at(0).attributes('href')).toBe('/');
- expect(links.at(1).attributes('href')).toBe('#');
- });
-
- it('the link text is calculated by nameGenerator', () => {
- expect(nameGenerator).toHaveBeenCalledTimes(2);
- });
- });
-});