Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/registry/explorer/pages')
-rw-r--r--spec/frontend/registry/explorer/pages/details_spec.js336
-rw-r--r--spec/frontend/registry/explorer/pages/index_spec.js4
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js341
3 files changed, 446 insertions, 235 deletions
diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js
index c09b7e0c067..d307dfe590c 100644
--- a/spec/frontend/registry/explorer/pages/details_spec.js
+++ b/spec/frontend/registry/explorer/pages/details_spec.js
@@ -1,5 +1,8 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlPagination } from '@gitlab/ui';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlKeysetPagination } from '@gitlab/ui';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/details.vue';
import DeleteAlert from '~/registry/explorer/components/details_page/delete_alert.vue';
@@ -8,25 +11,28 @@ import DetailsHeader from '~/registry/explorer/components/details_page/details_h
import TagsLoader from '~/registry/explorer/components/details_page/tags_loader.vue';
import TagsList from '~/registry/explorer/components/details_page/tags_list.vue';
import EmptyTagsState from '~/registry/explorer/components/details_page/empty_tags_state.vue';
-import { createStore } from '~/registry/explorer/stores/';
+
+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 {
- SET_MAIN_LOADING,
- SET_TAGS_LIST_SUCCESS,
- SET_TAGS_PAGINATION,
- SET_INITIAL_STATE,
- SET_IMAGE_DETAILS,
-} from '~/registry/explorer/stores/mutation_types';
-
-import { tagsListResponse, imageDetailsMock } from '../mock_data';
+ graphQLImageDetailsMock,
+ graphQLImageDetailsEmptyTagsMock,
+ graphQLDeleteImageRepositoryTagsMock,
+ containerRepositoryMock,
+ tagsMock,
+ tagsPageInfo,
+} from '../mock_data';
import { DeleteModal } from '../stubs';
+const localVue = createLocalVue();
+
describe('Details Page', () => {
let wrapper;
- let dispatchSpy;
- let store;
+ let apolloProvider;
const findDeleteModal = () => wrapper.find(DeleteModal);
- const findPagination = () => wrapper.find(GlPagination);
+ const findPagination = () => wrapper.find(GlKeysetPagination);
const findTagsLoader = () => wrapper.find(TagsLoader);
const findTagsList = () => wrapper.find(TagsList);
const findDeleteAlert = () => wrapper.find(DeleteAlert);
@@ -36,15 +42,46 @@ describe('Details Page', () => {
const routeId = 1;
+ const breadCrumbState = {
+ updateName: jest.fn(),
+ };
+
+ const cleanTags = tagsMock.map(t => {
+ const result = { ...t };
+ // eslint-disable-next-line no-underscore-dangle
+ delete result.__typename;
+ return result;
+ });
+
+ const waitForApolloRequestRender = async () => {
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+ };
+
const tagsArrayToSelectedTags = tags =>
tags.reduce((acc, c) => {
acc[c.name] = true;
return acc;
}, {});
- const mountComponent = ({ options } = {}) => {
+ const mountComponent = ({
+ resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()),
+ mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock),
+ options,
+ config = {},
+ } = {}) => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [
+ [getContainerRepositoryDetailsQuery, resolver],
+ [deleteContainerRepositoryTagsMutation, mutationResolver],
+ ];
+
+ apolloProvider = createMockApollo(requestHandlers);
+
wrapper = shallowMount(component, {
- store,
+ localVue,
+ apolloProvider,
stubs: {
DeleteModal,
},
@@ -55,17 +92,17 @@ describe('Details Page', () => {
},
},
},
+ provide() {
+ return {
+ breadCrumbState,
+ config,
+ };
+ },
...options,
});
};
beforeEach(() => {
- store = createStore();
- dispatchSpy = jest.spyOn(store, 'dispatch');
- dispatchSpy.mockResolvedValue();
- store.commit(SET_TAGS_LIST_SUCCESS, tagsListResponse.data);
- store.commit(SET_TAGS_PAGINATION, tagsListResponse.headers);
- store.commit(SET_IMAGE_DETAILS, imageDetailsMock);
jest.spyOn(Tracking, 'event');
});
@@ -74,85 +111,90 @@ describe('Details Page', () => {
wrapper = null;
});
- describe('lifecycle events', () => {
- it('calls the appropriate action on mount', () => {
- mountComponent();
- expect(dispatchSpy).toHaveBeenCalledWith('requestImageDetailsAndTagsList', routeId);
- });
- });
-
describe('when isLoading is true', () => {
- beforeEach(() => {
- store.commit(SET_MAIN_LOADING, true);
+ it('shows the loader', () => {
mountComponent();
- });
-
- afterEach(() => store.commit(SET_MAIN_LOADING, false));
- it('shows the loader', () => {
expect(findTagsLoader().exists()).toBe(true);
});
it('does not show the list', () => {
+ mountComponent();
+
expect(findTagsList().exists()).toBe(false);
});
it('does not show pagination', () => {
+ mountComponent();
+
expect(findPagination().exists()).toBe(false);
});
});
describe('when the list of tags is empty', () => {
- beforeEach(() => {
- store.commit(SET_TAGS_LIST_SUCCESS, []);
- mountComponent();
- });
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock);
+
+ it('has the empty state', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
- it('has the empty state', () => {
expect(findEmptyTagsState().exists()).toBe(true);
});
- it('does not show the loader', () => {
+ it('does not show the loader', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
expect(findTagsLoader().exists()).toBe(false);
});
- it('does not show the list', () => {
+ it('does not show the list', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
expect(findTagsList().exists()).toBe(false);
});
});
describe('list', () => {
- beforeEach(() => {
+ it('exists', async () => {
mountComponent();
- });
- it('exists', () => {
+ await waitForApolloRequestRender();
+
expect(findTagsList().exists()).toBe(true);
});
- it('has the correct props bound', () => {
+ it('has the correct props bound', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findTagsList().props()).toMatchObject({
isMobile: false,
- tags: store.state.tags,
+ tags: cleanTags,
});
});
describe('deleteEvent', () => {
describe('single item', () => {
let tagToBeDeleted;
- beforeEach(() => {
- [tagToBeDeleted] = store.state.tags;
+ beforeEach(async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ [tagToBeDeleted] = cleanTags;
findTagsList().vm.$emit('delete', { [tagToBeDeleted.name]: true });
});
- it('open the modal', () => {
+ it('open the modal', async () => {
expect(DeleteModal.methods.show).toHaveBeenCalled();
});
- it('maps the selection to itemToBeDeleted', () => {
- expect(wrapper.vm.itemsToBeDeleted).toEqual([tagToBeDeleted]);
- });
-
it('tracks a single delete event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: 'registry_tag_delete',
@@ -161,18 +203,18 @@ describe('Details Page', () => {
});
describe('multiple items', () => {
- beforeEach(() => {
- findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags));
+ beforeEach(async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(cleanTags));
});
it('open the modal', () => {
expect(DeleteModal.methods.show).toHaveBeenCalled();
});
- it('maps the selection to itemToBeDeleted', () => {
- expect(wrapper.vm.itemsToBeDeleted).toEqual(store.state.tags);
- });
-
it('tracks a single delete event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: 'bulk_registry_tag_delete',
@@ -183,40 +225,77 @@ describe('Details Page', () => {
});
describe('pagination', () => {
- beforeEach(() => {
+ it('exists', async () => {
mountComponent();
- });
- it('exists', () => {
+ await waitForApolloRequestRender();
+
expect(findPagination().exists()).toBe(true);
});
- it('is wired to the correct pagination props', () => {
- const pagination = findPagination();
- expect(pagination.props('perPage')).toBe(store.state.tagsPagination.perPage);
- expect(pagination.props('totalItems')).toBe(store.state.tagsPagination.total);
- expect(pagination.props('value')).toBe(store.state.tagsPagination.page);
+ it('is hidden when there are no more pages', async () => {
+ mountComponent({ resolver: jest.fn().mockResolvedValue(graphQLImageDetailsEmptyTagsMock) });
+
+ await waitForApolloRequestRender();
+
+ expect(findPagination().exists()).toBe(false);
});
- it('fetch the data from the API when the v-model changes', () => {
- dispatchSpy.mockResolvedValue();
- findPagination().vm.$emit(GlPagination.model.event, 2);
- expect(store.dispatch).toHaveBeenCalledWith('requestTagsList', {
- page: 2,
+ it('is wired to the correct pagination props', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ expect(findPagination().props()).toMatchObject({
+ hasNextPage: tagsPageInfo.hasNextPage,
+ hasPreviousPage: tagsPageInfo.hasPreviousPage,
});
});
+
+ it('fetch next page when user clicks next', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock());
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findPagination().vm.$emit('next');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ after: tagsPageInfo.endCursor }),
+ );
+ });
+
+ it('fetch previous page when user clicks prev', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock());
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findPagination().vm.$emit('prev');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ first: null, before: tagsPageInfo.startCursor }),
+ );
+ });
});
describe('modal', () => {
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findDeleteModal().exists()).toBe(true);
});
describe('cancel event', () => {
- it('tracks cancel_delete', () => {
+ it('tracks cancel_delete', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
findDeleteModal().vm.$emit('cancel');
+
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', {
label: 'registry_tag_delete',
});
@@ -224,45 +303,62 @@ describe('Details Page', () => {
});
describe('confirmDelete event', () => {
+ let mutationResolver;
+
+ beforeEach(() => {
+ mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock);
+ mountComponent({ mutationResolver });
+
+ return waitForApolloRequestRender();
+ });
describe('when one item is selected to be deleted', () => {
- beforeEach(() => {
- mountComponent();
- findTagsList().vm.$emit('delete', { [store.state.tags[0].name]: true });
- });
+ it('calls apollo mutation with the right parameters', async () => {
+ findTagsList().vm.$emit('delete', { [cleanTags[0].name]: true });
+
+ await wrapper.vm.$nextTick();
- it('dispatch requestDeleteTag with the right parameters', () => {
findDeleteModal().vm.$emit('confirmDelete');
- expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTag', {
- tag: store.state.tags[0],
- });
+
+ expect(mutationResolver).toHaveBeenCalledWith(
+ expect.objectContaining({ tagNames: [cleanTags[0].name] }),
+ );
});
});
describe('when more than one item is selected to be deleted', () => {
- beforeEach(() => {
- mountComponent();
- findTagsList().vm.$emit('delete', tagsArrayToSelectedTags(store.state.tags));
- });
+ it('calls apollo mutation with the right parameters', async () => {
+ findTagsList().vm.$emit('delete', { ...tagsArrayToSelectedTags(tagsMock) });
+
+ await wrapper.vm.$nextTick();
- it('dispatch requestDeleteTags with the right parameters', () => {
findDeleteModal().vm.$emit('confirmDelete');
- expect(dispatchSpy).toHaveBeenCalledWith('requestDeleteTags', {
- ids: store.state.tags.map(t => t.name),
- });
+
+ expect(mutationResolver).toHaveBeenCalledWith(
+ expect.objectContaining({ tagNames: tagsMock.map(t => t.name) }),
+ );
});
});
});
});
describe('Header', () => {
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
expect(findDetailsHeader().exists()).toBe(true);
});
- it('has the correct props', () => {
+ it('has the correct props', async () => {
mountComponent();
- expect(findDetailsHeader().props()).toEqual({ imageName: imageDetailsMock.name });
+
+ await waitForApolloRequestRender();
+ expect(findDetailsHeader().props('image')).toMatchObject({
+ name: containerRepositoryMock.name,
+ project: {
+ visibility: containerRepositoryMock.project.visibility,
+ },
+ });
});
});
@@ -273,20 +369,25 @@ describe('Details Page', () => {
};
const deleteAlertType = 'success_tag';
- it('exists', () => {
+ it('exists', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
expect(findDeleteAlert().exists()).toBe(true);
});
- it('has the correct props', () => {
- store.commit(SET_INITIAL_STATE, { ...config });
+ it('has the correct props', async () => {
mountComponent({
options: {
data: () => ({
deleteAlertType,
}),
},
+ config,
});
+
+ await waitForApolloRequestRender();
+
expect(findDeleteAlert().props()).toEqual({ ...config, deleteAlertType });
});
});
@@ -298,30 +399,38 @@ describe('Details Page', () => {
};
describe('when expiration_policy_started is not null', () => {
+ let resolver;
+
beforeEach(() => {
- store.commit(SET_IMAGE_DETAILS, {
- ...imageDetailsMock,
- cleanup_policy_started_at: Date.now().toString(),
- });
+ resolver = jest.fn().mockResolvedValue(
+ graphQLImageDetailsMock({
+ expirationPolicyStartedAt: Date.now().toString(),
+ }),
+ );
});
- it('exists', () => {
- mountComponent();
+ it('exists', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(true);
});
- it('has the correct props', () => {
- store.commit(SET_INITIAL_STATE, { ...config });
+ it('has the correct props', async () => {
+ mountComponent({ resolver, config });
- mountComponent();
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().props()).toEqual({ ...config });
});
it('dismiss hides the component', async () => {
- mountComponent();
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(true);
+
findPartialCleanupAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
@@ -331,11 +440,22 @@ describe('Details Page', () => {
});
describe('when expiration_policy_started is null', () => {
- it('the component is hidden', () => {
+ it('the component is hidden', async () => {
mountComponent();
+ await waitForApolloRequestRender();
expect(findPartialCleanupAlert().exists()).toBe(false);
});
});
});
+
+ describe('Breadcrumb connection', () => {
+ it('when the details are fetched updates the name', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
+ expect(breadCrumbState.updateName).toHaveBeenCalledWith(containerRepositoryMock.name);
+ });
+ });
});
diff --git a/spec/frontend/registry/explorer/pages/index_spec.js b/spec/frontend/registry/explorer/pages/index_spec.js
index 1dc5376cacf..b5f718b3e61 100644
--- a/spec/frontend/registry/explorer/pages/index_spec.js
+++ b/spec/frontend/registry/explorer/pages/index_spec.js
@@ -1,16 +1,13 @@
import { shallowMount } from '@vue/test-utils';
import component from '~/registry/explorer/pages/index.vue';
-import { createStore } from '~/registry/explorer/stores/';
describe('List Page', () => {
let wrapper;
- let store;
const findRouterView = () => wrapper.find({ ref: 'router-view' });
const mountComponent = () => {
wrapper = shallowMount(component, {
- store,
stubs: {
RouterView: true,
},
@@ -18,7 +15,6 @@ describe('List Page', () => {
};
beforeEach(() => {
- store = createStore();
mountComponent();
});
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index b24422adb03..7d32a667011 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -1,5 +1,7 @@
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
import { GlSkeletonLoader, GlSprintf, GlAlert, GlSearchBoxByClick } from '@gitlab/ui';
+import createMockApollo from 'jest/helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/list.vue';
@@ -9,27 +11,35 @@ import ProjectEmptyState from '~/registry/explorer/components/list_page/project_
import RegistryHeader from '~/registry/explorer/components/list_page/registry_header.vue';
import ImageList from '~/registry/explorer/components/list_page/image_list.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
-import { createStore } from '~/registry/explorer/stores/';
-import {
- SET_MAIN_LOADING,
- SET_IMAGES_LIST_SUCCESS,
- SET_PAGINATION,
- SET_INITIAL_STATE,
-} from '~/registry/explorer/stores/mutation_types';
+
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
IMAGE_REPOSITORY_LIST_LABEL,
SEARCH_PLACEHOLDER_TEXT,
} from '~/registry/explorer/constants';
-import { imagesListResponse } from '../mock_data';
+
+import getProjectContainerRepositoriesQuery from '~/registry/explorer/graphql/queries/get_project_container_repositories.query.graphql';
+import getGroupContainerRepositoriesQuery from '~/registry/explorer/graphql/queries/get_group_container_repositories.query.graphql';
+import deleteContainerRepositoryMutation from '~/registry/explorer/graphql/mutations/delete_container_repository.mutation.graphql';
+
+import {
+ graphQLImageListMock,
+ graphQLImageDeleteMock,
+ deletedContainerRepository,
+ graphQLImageDeleteMockError,
+ graphQLEmptyImageListMock,
+ graphQLEmptyGroupImageListMock,
+ pageInfo,
+} from '../mock_data';
import { GlModal, GlEmptyState } from '../stubs';
import { $toast } from '../../shared/mocks';
+const localVue = createLocalVue();
+
describe('List Page', () => {
let wrapper;
- let dispatchSpy;
- let store;
+ let apolloProvider;
const findDeleteModal = () => wrapper.find(GlModal);
const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader);
@@ -47,9 +57,31 @@ describe('List Page', () => {
const findSearchBox = () => wrapper.find(GlSearchBoxByClick);
const findEmptySearchMessage = () => wrapper.find('[data-testid="emptySearch"]');
- const mountComponent = ({ mocks } = {}) => {
+ const waitForApolloRequestRender = async () => {
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+ };
+
+ const mountComponent = ({
+ mocks,
+ resolver = jest.fn().mockResolvedValue(graphQLImageListMock),
+ groupResolver = jest.fn().mockResolvedValue(graphQLImageListMock),
+ mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock),
+ config = {},
+ } = {}) => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [
+ [getProjectContainerRepositoriesQuery, resolver],
+ [getGroupContainerRepositoriesQuery, groupResolver],
+ [deleteContainerRepositoryMutation, mutationResolver],
+ ];
+
+ apolloProvider = createMockApollo(requestHandlers);
+
wrapper = shallowMount(component, {
- store,
+ localVue,
+ apolloProvider,
stubs: {
GlModal,
GlEmptyState,
@@ -64,42 +96,27 @@ describe('List Page', () => {
},
...mocks,
},
+ provide() {
+ return {
+ config,
+ };
+ },
});
};
- beforeEach(() => {
- store = createStore();
- dispatchSpy = jest.spyOn(store, 'dispatch');
- dispatchSpy.mockResolvedValue();
- store.commit(SET_IMAGES_LIST_SUCCESS, imagesListResponse.data);
- store.commit(SET_PAGINATION, imagesListResponse.headers);
- });
-
afterEach(() => {
wrapper.destroy();
});
- describe('API calls', () => {
- it.each`
- imageList | name | called
- ${[]} | ${'foo'} | ${['requestImagesList']}
- ${imagesListResponse.data} | ${undefined} | ${['requestImagesList']}
- ${imagesListResponse.data} | ${'foo'} | ${undefined}
- `(
- 'with images equal $imageList and name $name dispatch calls $called',
- ({ imageList, name, called }) => {
- store.commit(SET_IMAGES_LIST_SUCCESS, imageList);
- dispatchSpy.mockClear();
- mountComponent({ mocks: { $route: { name } } });
-
- expect(dispatchSpy.mock.calls[0]).toEqual(called);
- },
- );
- });
-
- it('contains registry header', () => {
+ it('contains registry header', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findRegistryHeader().exists()).toBe(true);
+ expect(findRegistryHeader().props()).toMatchObject({
+ imagesCount: 2,
+ });
});
describe('connection error', () => {
@@ -109,88 +126,100 @@ describe('List Page', () => {
helpPagePath: 'bar',
};
- beforeEach(() => {
- store.commit(SET_INITIAL_STATE, config);
- mountComponent();
- });
-
- afterEach(() => {
- store.commit(SET_INITIAL_STATE, {});
- });
-
it('should show an empty state', () => {
+ mountComponent({ config });
+
expect(findEmptyState().exists()).toBe(true);
});
it('empty state should have an svg-path', () => {
- expect(findEmptyState().attributes('svg-path')).toBe(config.containersErrorImage);
+ mountComponent({ config });
+
+ expect(findEmptyState().props('svgPath')).toBe(config.containersErrorImage);
});
it('empty state should have a description', () => {
- expect(findEmptyState().html()).toContain('connection error');
+ mountComponent({ config });
+
+ expect(findEmptyState().props('title')).toContain('connection error');
});
it('should not show the loading or default state', () => {
+ mountComponent({ config });
+
expect(findSkeletonLoader().exists()).toBe(false);
expect(findImageList().exists()).toBe(false);
});
});
describe('isLoading is true', () => {
- beforeEach(() => {
- store.commit(SET_MAIN_LOADING, true);
+ it('shows the skeleton loader', () => {
mountComponent();
- });
-
- afterEach(() => store.commit(SET_MAIN_LOADING, false));
- it('shows the skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(true);
});
it('imagesList is not visible', () => {
+ mountComponent();
+
expect(findImageList().exists()).toBe(false);
});
it('cli commands is not visible', () => {
+ mountComponent();
+
expect(findCliCommands().exists()).toBe(false);
});
});
describe('list is empty', () => {
- beforeEach(() => {
- store.commit(SET_IMAGES_LIST_SUCCESS, []);
- mountComponent();
- return waitForPromises();
- });
+ describe('project page', () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLEmptyImageListMock);
- it('cli commands is not visible', () => {
- expect(findCliCommands().exists()).toBe(false);
- });
+ it('cli commands is not visible', async () => {
+ mountComponent({ resolver });
- it('project empty state is visible', () => {
- expect(findProjectEmptyState().exists()).toBe(true);
- });
+ await waitForApolloRequestRender();
- describe('is group page is true', () => {
- beforeEach(() => {
- store.commit(SET_INITIAL_STATE, { isGroupPage: true });
- mountComponent();
+ expect(findCliCommands().exists()).toBe(false);
});
- afterEach(() => {
- store.commit(SET_INITIAL_STATE, { isGroupPage: undefined });
+ it('project empty state is visible', async () => {
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ expect(findProjectEmptyState().exists()).toBe(true);
});
+ });
+ describe('group page', () => {
+ const groupResolver = jest.fn().mockResolvedValue(graphQLEmptyGroupImageListMock);
+
+ const config = {
+ isGroupPage: true,
+ };
+
+ it('group empty state is visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
- it('group empty state is visible', () => {
expect(findGroupEmptyState().exists()).toBe(true);
});
- it('cli commands is not visible', () => {
+ it('cli commands is not visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
+
expect(findCliCommands().exists()).toBe(false);
});
- it('list header is not visible', () => {
+ it('list header is not visible', async () => {
+ mountComponent({ groupResolver, config });
+
+ await waitForApolloRequestRender();
+
expect(findListHeader().exists()).toBe(false);
});
});
@@ -198,55 +227,91 @@ describe('List Page', () => {
describe('list is not empty', () => {
describe('unfiltered state', () => {
- beforeEach(() => {
+ it('quick start is visible', async () => {
mountComponent();
- });
- it('quick start is visible', () => {
+ await waitForApolloRequestRender();
+
expect(findCliCommands().exists()).toBe(true);
});
- it('list component is visible', () => {
+ it('list component is visible', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
expect(findImageList().exists()).toBe(true);
});
- it('list header is visible', () => {
+ it('list header is visible', async () => {
+ mountComponent();
+
+ await waitForApolloRequestRender();
+
const header = findListHeader();
expect(header.exists()).toBe(true);
expect(header.text()).toBe(IMAGE_REPOSITORY_LIST_LABEL);
});
describe('delete image', () => {
- const itemToDelete = { path: 'bar' };
- it('should call deleteItem when confirming deletion', () => {
- dispatchSpy.mockResolvedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
+ const deleteImage = async () => {
+ await wrapper.vm.$nextTick();
+
+ findImageList().vm.$emit('delete', deletedContainerRepository);
findDeleteModal().vm.$emit('ok');
- expect(store.dispatch).toHaveBeenCalledWith(
- 'requestDeleteImage',
- wrapper.vm.itemToDelete,
+
+ await waitForApolloRequestRender();
+ };
+
+ it('should call deleteItem when confirming deletion', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
+ expect(wrapper.vm.itemToDelete).toEqual(deletedContainerRepository);
+ expect(mutationResolver).toHaveBeenCalledWith({ id: deletedContainerRepository.id });
+
+ const updatedImage = findImageList()
+ .props('images')
+ .find(i => i.id === deletedContainerRepository.id);
+
+ expect(updatedImage.status).toBe(deletedContainerRepository.status);
+ });
+
+ it('should show a success alert when delete request is successful', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMock);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
+ const alert = findDeleteAlert();
+ expect(alert.exists()).toBe(true);
+ expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
+ DELETE_IMAGE_SUCCESS_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
);
});
- it('should show a success alert when delete request is successful', () => {
- dispatchSpy.mockResolvedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
- return wrapper.vm.handleDeleteImage().then(() => {
+ describe('when delete request fails it shows an alert', () => {
+ it('user recoverable error', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(graphQLImageDeleteMockError);
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
- DELETE_IMAGE_SUCCESS_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
+ DELETE_IMAGE_ERROR_MESSAGE.replace('%{title}', wrapper.vm.itemToDelete.path),
);
});
- });
- it('should show an error alert when delete request fails', () => {
- dispatchSpy.mockRejectedValue();
- findImageList().vm.$emit('delete', itemToDelete);
- expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
- return wrapper.vm.handleDeleteImage().then(() => {
+ it('network error', async () => {
+ const mutationResolver = jest.fn().mockRejectedValue();
+ mountComponent({ mutationResolver });
+
+ await deleteImage();
+
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
expect(alert.text().replace(/\s\s+/gm, ' ')).toBe(
@@ -258,38 +323,68 @@ describe('List Page', () => {
});
describe('search', () => {
- it('has a search box element', () => {
+ const doSearch = async () => {
+ await waitForApolloRequestRender();
+ findSearchBox().vm.$emit('submit', 'centos6');
+ await wrapper.vm.$nextTick();
+ };
+
+ it('has a search box element', async () => {
mountComponent();
+
+ await waitForApolloRequestRender();
+
const searchBox = findSearchBox();
expect(searchBox.exists()).toBe(true);
expect(searchBox.attributes('placeholder')).toBe(SEARCH_PLACEHOLDER_TEXT);
});
- it('performs a search', () => {
- mountComponent();
- findSearchBox().vm.$emit('submit', 'foo');
- expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', {
- name: 'foo',
- });
+ it('performs a search', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await doSearch();
+
+ expect(resolver).toHaveBeenCalledWith(expect.objectContaining({ name: 'centos6' }));
});
- it('when search result is empty displays an empty search message', () => {
- mountComponent();
- store.commit(SET_IMAGES_LIST_SUCCESS, []);
- return wrapper.vm.$nextTick().then(() => {
- expect(findEmptySearchMessage().exists()).toBe(true);
- });
+ it('when search result is empty displays an empty search message', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ resolver.mockResolvedValue(graphQLEmptyImageListMock);
+
+ await doSearch();
+
+ expect(findEmptySearchMessage().exists()).toBe(true);
});
});
describe('pagination', () => {
- it('pageChange event triggers the appropriate store function', () => {
- mountComponent();
- findImageList().vm.$emit('pageChange', 2);
- expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', {
- pagination: { page: 2 },
- name: wrapper.vm.search,
- });
+ it('prev-page event triggers a fetchMore request', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findImageList().vm.$emit('prev-page');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ first: null, before: pageInfo.startCursor }),
+ );
+ });
+
+ it('next-page event triggers a fetchMore request', async () => {
+ const resolver = jest.fn().mockResolvedValue(graphQLImageListMock);
+ mountComponent({ resolver });
+
+ await waitForApolloRequestRender();
+
+ findImageList().vm.$emit('next-page');
+
+ expect(resolver).toHaveBeenCalledWith(
+ expect.objectContaining({ after: pageInfo.endCursor }),
+ );
});
});
});
@@ -324,11 +419,11 @@ describe('List Page', () => {
beforeEach(() => {
jest.spyOn(Tracking, 'event');
- dispatchSpy.mockResolvedValue();
});
it('send an event when delete button is clicked', () => {
findImageList().vm.$emit('delete', {});
+
testTrackingCall('click_button');
});