diff options
Diffstat (limited to 'spec/frontend/packages_and_registries/container_registry')
7 files changed, 140 insertions, 292 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); - }); - }); -}); diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js index 9b821ba8ef3..7992bead60a 100644 --- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js +++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js @@ -1,4 +1,4 @@ -import { GlKeysetPagination } from '@gitlab/ui'; +import { GlKeysetPagination, GlEmptyState } from '@gitlab/ui'; import { shallowMount, createLocalVue } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import { nextTick } from 'vue'; @@ -8,7 +8,6 @@ import axios from '~/lib/utils/axios_utils'; import DeleteImage from '~/packages_and_registries/container_registry/explorer/components/delete_image.vue'; import DeleteAlert from '~/packages_and_registries/container_registry/explorer/components/details_page/delete_alert.vue'; import DetailsHeader from '~/packages_and_registries/container_registry/explorer/components/details_page/details_header.vue'; -import EmptyTagsState from '~/packages_and_registries/container_registry/explorer/components/details_page/empty_state.vue'; import PartialCleanupAlert from '~/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert.vue'; import StatusAlert from '~/packages_and_registries/container_registry/explorer/components/details_page/status_alert.vue'; import TagsList from '~/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue'; @@ -20,6 +19,8 @@ import { ALERT_DANGER_IMAGE, MISSING_OR_DELETED_IMAGE_BREADCRUMB, ROOT_IMAGE_TEXT, + MISSING_OR_DELETED_IMAGE_TITLE, + MISSING_OR_DELETED_IMAGE_MESSAGE, } from '~/packages_and_registries/container_registry/explorer/constants'; import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql'; import getContainerRepositoryDetailsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_details.query.graphql'; @@ -50,7 +51,7 @@ describe('Details Page', () => { const findTagsList = () => wrapper.find(TagsList); const findDeleteAlert = () => wrapper.find(DeleteAlert); const findDetailsHeader = () => wrapper.find(DetailsHeader); - const findEmptyState = () => wrapper.find(EmptyTagsState); + const findEmptyState = () => wrapper.find(GlEmptyState); const findPartialCleanupAlert = () => wrapper.find(PartialCleanupAlert); const findStatusAlert = () => wrapper.find(StatusAlert); const findDeleteImage = () => wrapper.find(DeleteImage); @@ -61,6 +62,10 @@ describe('Details Page', () => { updateName: jest.fn(), }; + const defaultConfig = { + noContainersImage: 'noContainersImage', + }; + const cleanTags = tagsMock.map((t) => { const result = { ...t }; // eslint-disable-next-line no-underscore-dangle @@ -78,7 +83,7 @@ describe('Details Page', () => { mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock), tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock)), options, - config = {}, + config = defaultConfig, } = {}) => { localVue.use(VueApollo); @@ -154,7 +159,11 @@ describe('Details Page', () => { await waitForApolloRequestRender(); - expect(findEmptyState().exists()).toBe(true); + expect(findEmptyState().props()).toMatchObject({ + description: MISSING_OR_DELETED_IMAGE_MESSAGE, + svgPath: defaultConfig.noContainersImage, + title: MISSING_OR_DELETED_IMAGE_TITLE, + }); }); }); |