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/list_spec.js')
-rw-r--r--spec/frontend/registry/explorer/pages/list_spec.js269
1 files changed, 162 insertions, 107 deletions
diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js
index f69b849521d..97742b9e9b3 100644
--- a/spec/frontend/registry/explorer/pages/list_spec.js
+++ b/spec/frontend/registry/explorer/pages/list_spec.js
@@ -1,47 +1,53 @@
-import VueRouter from 'vue-router';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { GlPagination, GlSkeletonLoader, GlSprintf, GlAlert } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { GlSkeletonLoader, GlSprintf, GlAlert, GlSearchBoxByClick } from '@gitlab/ui';
import Tracking from '~/tracking';
+import waitForPromises from 'helpers/wait_for_promises';
import component from '~/registry/explorer/pages/list.vue';
import QuickstartDropdown from '~/registry/explorer/components/quickstart_dropdown.vue';
import GroupEmptyState from '~/registry/explorer/components/group_empty_state.vue';
import ProjectEmptyState from '~/registry/explorer/components/project_empty_state.vue';
import ProjectPolicyAlert from '~/registry/explorer/components/project_policy_alert.vue';
-import store from '~/registry/explorer/stores/';
-import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/';
+import ImageList from '~/registry/explorer/components/image_list.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 { GlModal, GlEmptyState } from '../stubs';
import { $toast } from '../../shared/mocks';
-const localVue = createLocalVue();
-localVue.use(VueRouter);
-
describe('List Page', () => {
let wrapper;
let dispatchSpy;
+ let store;
- const findDeleteBtn = () => wrapper.find({ ref: 'deleteImageButton' });
const findDeleteModal = () => wrapper.find(GlModal);
const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader);
const findImagesList = () => wrapper.find({ ref: 'imagesList' });
- const findRowItems = () => wrapper.findAll({ ref: 'rowItem' });
+
const findEmptyState = () => wrapper.find(GlEmptyState);
- const findDetailsLink = () => wrapper.find({ ref: 'detailsLink' });
- const findClipboardButton = () => wrapper.find({ ref: 'clipboardButton' });
- const findPagination = () => wrapper.find(GlPagination);
+
const findQuickStartDropdown = () => wrapper.find(QuickstartDropdown);
const findProjectEmptyState = () => wrapper.find(ProjectEmptyState);
const findGroupEmptyState = () => wrapper.find(GroupEmptyState);
const findProjectPolicyAlert = () => wrapper.find(ProjectPolicyAlert);
const findDeleteAlert = () => wrapper.find(GlAlert);
+ const findImageList = () => wrapper.find(ImageList);
+ const findListHeader = () => wrapper.find('[data-testid="listHeader"]');
+ const findSearchBox = () => wrapper.find(GlSearchBoxByClick);
+ const findEmptySearchMessage = () => wrapper.find('[data-testid="emptySearch"]');
- beforeEach(() => {
+ const mountComponent = ({ mocks } = {}) => {
wrapper = shallowMount(component, {
- localVue,
store,
stubs: {
GlModal,
@@ -50,10 +56,20 @@ describe('List Page', () => {
},
mocks: {
$toast,
+ $route: {
+ name: 'foo',
+ },
+ ...mocks,
},
});
+ };
+
+ beforeEach(() => {
+ store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
- store.dispatch('receiveImagesListSuccess', imagesListResponse);
+ dispatchSpy.mockResolvedValue();
+ store.commit(SET_IMAGES_LIST_SUCCESS, imagesListResponse.data);
+ store.commit(SET_PAGINATION, imagesListResponse.headers);
});
afterEach(() => {
@@ -61,17 +77,38 @@ describe('List Page', () => {
});
describe('Expiration policy notification', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
it('shows up on project page', () => {
expect(findProjectPolicyAlert().exists()).toBe(true);
});
it('does show up on group page', () => {
- store.dispatch('setInitialState', { isGroupPage: true });
+ store.commit(SET_INITIAL_STATE, { isGroupPage: true });
return wrapper.vm.$nextTick().then(() => {
expect(findProjectPolicyAlert().exists()).toBe(false);
});
});
});
+ 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);
+ },
+ );
+ });
+
describe('connection error', () => {
const config = {
characterError: true,
@@ -79,12 +116,13 @@ describe('List Page', () => {
helpPagePath: 'bar',
};
- beforeAll(() => {
- store.dispatch('setInitialState', config);
+ beforeEach(() => {
+ store.commit(SET_INITIAL_STATE, config);
+ mountComponent();
});
- afterAll(() => {
- store.dispatch('setInitialState', {});
+ afterEach(() => {
+ store.commit(SET_INITIAL_STATE, {});
});
it('should show an empty state', () => {
@@ -106,9 +144,12 @@ describe('List Page', () => {
});
describe('isLoading is true', () => {
- beforeAll(() => store.commit(SET_MAIN_LOADING, true));
+ beforeEach(() => {
+ store.commit(SET_MAIN_LOADING, true);
+ mountComponent();
+ });
- afterAll(() => store.commit(SET_MAIN_LOADING, false));
+ afterEach(() => store.commit(SET_MAIN_LOADING, false));
it('shows the skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(true);
@@ -125,7 +166,9 @@ describe('List Page', () => {
describe('list is empty', () => {
beforeEach(() => {
- store.dispatch('receiveImagesListSuccess', { data: [] });
+ store.commit(SET_IMAGES_LIST_SUCCESS, []);
+ mountComponent();
+ return waitForPromises();
});
it('quick start is not visible', () => {
@@ -137,12 +180,13 @@ describe('List Page', () => {
});
describe('is group page is true', () => {
- beforeAll(() => {
- store.dispatch('setInitialState', { isGroupPage: true });
+ beforeEach(() => {
+ store.commit(SET_INITIAL_STATE, { isGroupPage: true });
+ mountComponent();
});
- afterAll(() => {
- store.dispatch('setInitialState', { isGroupPage: undefined });
+ afterEach(() => {
+ store.commit(SET_INITIAL_STATE, { isGroupPage: undefined });
});
it('group empty state is visible', () => {
@@ -152,50 +196,39 @@ describe('List Page', () => {
it('quick start is not visible', () => {
expect(findQuickStartDropdown().exists()).toBe(false);
});
+
+ it('list header is not visible', () => {
+ expect(findListHeader().exists()).toBe(false);
+ });
});
});
describe('list is not empty', () => {
- it('quick start is visible', () => {
- expect(findQuickStartDropdown().exists()).toBe(true);
- });
-
- describe('listElement', () => {
- let listElements;
- let firstElement;
-
+ describe('unfiltered state', () => {
beforeEach(() => {
- listElements = findRowItems();
- [firstElement] = store.state.images;
+ mountComponent();
});
- it('contains one list element for each image', () => {
- expect(listElements.length).toBe(store.state.images.length);
+ it('quick start is visible', () => {
+ expect(findQuickStartDropdown().exists()).toBe(true);
});
- it('contains a link to the details page', () => {
- const link = findDetailsLink();
- expect(link.html()).toContain(firstElement.path);
- expect(link.props('to').name).toBe('details');
+ it('list component is visible', () => {
+ expect(findImageList().exists()).toBe(true);
});
- it('contains a clipboard button', () => {
- const button = findClipboardButton();
- expect(button.exists()).toBe(true);
- expect(button.props('text')).toBe(firstElement.location);
- expect(button.props('title')).toBe(firstElement.location);
+ it('list header is visible', () => {
+ const header = findListHeader();
+ expect(header.exists()).toBe(true);
+ expect(header.text()).toBe(IMAGE_REPOSITORY_LIST_LABEL);
});
describe('delete image', () => {
- it('should be possible to delete a repo', () => {
- const deleteBtn = findDeleteBtn();
- expect(deleteBtn.exists()).toBe(true);
- });
-
+ const itemToDelete = { path: 'bar' };
it('should call deleteItem when confirming deletion', () => {
dispatchSpy.mockResolvedValue();
- findDeleteBtn().vm.$emit('click');
- expect(wrapper.vm.itemToDelete).not.toEqual({});
+ findImageList().vm.$emit('delete', itemToDelete);
+ expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
findDeleteModal().vm.$emit('ok');
expect(store.dispatch).toHaveBeenCalledWith(
'requestDeleteImage',
@@ -205,8 +238,8 @@ describe('List Page', () => {
it('should show a success alert when delete request is successful', () => {
dispatchSpy.mockResolvedValue();
- findDeleteBtn().vm.$emit('click');
- expect(wrapper.vm.itemToDelete).not.toEqual({});
+ findImageList().vm.$emit('delete', itemToDelete);
+ expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
return wrapper.vm.handleDeleteImage().then(() => {
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
@@ -218,8 +251,8 @@ describe('List Page', () => {
it('should show an error alert when delete request fails', () => {
dispatchSpy.mockRejectedValue();
- findDeleteBtn().vm.$emit('click');
- expect(wrapper.vm.itemToDelete).not.toEqual({});
+ findImageList().vm.$emit('delete', itemToDelete);
+ expect(wrapper.vm.itemToDelete).toEqual(itemToDelete);
return wrapper.vm.handleDeleteImage().then(() => {
const alert = findDeleteAlert();
expect(alert.exists()).toBe(true);
@@ -229,71 +262,93 @@ describe('List Page', () => {
});
});
});
+ });
- describe('pagination', () => {
- it('exists', () => {
- expect(findPagination().exists()).toBe(true);
- });
+ describe('search', () => {
+ it('has a search box element', () => {
+ mountComponent();
+ const searchBox = findSearchBox();
+ expect(searchBox.exists()).toBe(true);
+ expect(searchBox.attributes('placeholder')).toBe(SEARCH_PLACEHOLDER_TEXT);
+ });
- it('is wired to the correct pagination props', () => {
- const pagination = findPagination();
- expect(pagination.props('perPage')).toBe(store.state.pagination.perPage);
- expect(pagination.props('totalItems')).toBe(store.state.pagination.total);
- expect(pagination.props('value')).toBe(store.state.pagination.page);
+ it('performs a search', () => {
+ mountComponent();
+ findSearchBox().vm.$emit('submit', 'foo');
+ expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', {
+ name: 'foo',
});
+ });
- it('fetch the data from the API when the v-model changes', () => {
- dispatchSpy.mockReturnValue();
- wrapper.setData({ currentPage: 2 });
- return wrapper.vm.$nextTick().then(() => {
- expect(store.dispatch).toHaveBeenCalledWith('requestImagesList', { page: 2 });
- });
+ 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);
});
});
});
- describe('modal', () => {
- it('exists', () => {
- expect(findDeleteModal().exists()).toBe(true);
- });
-
- it('contains a description with the path of the item to delete', () => {
- wrapper.setData({ itemToDelete: { path: 'foo' } });
- return wrapper.vm.$nextTick().then(() => {
- expect(findDeleteModal().html()).toContain('foo');
+ 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,
});
});
});
+ });
- describe('tracking', () => {
- const testTrackingCall = action => {
- expect(Tracking.event).toHaveBeenCalledWith(undefined, action, {
- label: 'registry_repository_delete',
- });
- };
+ describe('modal', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
- beforeEach(() => {
- jest.spyOn(Tracking, 'event');
- dispatchSpy.mockResolvedValue();
- });
+ it('exists', () => {
+ expect(findDeleteModal().exists()).toBe(true);
+ });
- it('send an event when delete button is clicked', () => {
- const deleteBtn = findDeleteBtn();
- deleteBtn.vm.$emit('click');
- testTrackingCall('click_button');
+ it('contains a description with the path of the item to delete', () => {
+ wrapper.setData({ itemToDelete: { path: 'foo' } });
+ return wrapper.vm.$nextTick().then(() => {
+ expect(findDeleteModal().html()).toContain('foo');
});
+ });
+ });
- it('send an event when cancel is pressed on modal', () => {
- const deleteModal = findDeleteModal();
- deleteModal.vm.$emit('cancel');
- testTrackingCall('cancel_delete');
- });
+ describe('tracking', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
- it('send an event when confirm is clicked on modal', () => {
- const deleteModal = findDeleteModal();
- deleteModal.vm.$emit('ok');
- testTrackingCall('confirm_delete');
+ const testTrackingCall = action => {
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, action, {
+ label: 'registry_repository_delete',
});
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ dispatchSpy.mockResolvedValue();
+ });
+
+ it('send an event when delete button is clicked', () => {
+ findImageList().vm.$emit('delete', {});
+ testTrackingCall('click_button');
+ });
+
+ it('send an event when cancel is pressed on modal', () => {
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('cancel');
+ testTrackingCall('cancel_delete');
+ });
+
+ it('send an event when confirm is clicked on modal', () => {
+ const deleteModal = findDeleteModal();
+ deleteModal.vm.$emit('ok');
+ testTrackingCall('confirm_delete');
});
});
});