debian-mirror-gitlab/spec/frontend/registry/list/components/table_registry_spec.js
2020-03-13 15:44:24 +05:30

373 lines
12 KiB
JavaScript

import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import createFlash from '~/flash';
import Tracking from '~/tracking';
import tableRegistry from '~/registry/list/components/table_registry.vue';
import { repoPropsData } from '../mock_data';
import * as getters from '~/registry/list/stores/getters';
jest.mock('~/flash');
const [firstImage, secondImage] = repoPropsData.list;
const localVue = createLocalVue();
localVue.use(Vuex);
describe('table registry', () => {
let wrapper;
let store;
const findSelectAllCheckbox = () => wrapper.find('.js-select-all-checkbox > input');
const findSelectCheckboxes = () => wrapper.findAll('.js-select-checkbox > input');
const findDeleteButton = () => wrapper.find({ ref: 'bulkDeleteButton' });
const findDeleteButtonsRow = () => wrapper.findAll('.js-delete-registry-row');
const findPagination = () => wrapper.find('.js-registry-pagination');
const findDeleteModal = () => wrapper.find({ ref: 'deleteModal' });
const findImageId = () => wrapper.find({ ref: 'imageId' });
const bulkDeletePath = 'path';
const mountWithStore = config =>
mount(tableRegistry, {
...config,
store,
localVue,
});
beforeEach(() => {
store = new Vuex.Store({
state: {
isDeleteDisabled: false,
},
getters,
});
wrapper = mountWithStore({
propsData: {
repo: repoPropsData,
canDeleteRepo: true,
},
});
});
afterEach(() => {
wrapper.destroy();
});
describe('rendering', () => {
it('should render a table with the registry list', () => {
expect(wrapper.findAll('.registry-image-row').length).toEqual(repoPropsData.list.length);
});
it('should render registry tag', () => {
const tds = wrapper.findAll('.registry-image-row td');
expect(tds.at(0).classes()).toContain('check');
expect(tds.at(1).html()).toContain(repoPropsData.list[0].tag);
expect(tds.at(2).html()).toContain(repoPropsData.list[0].shortRevision);
expect(tds.at(3).html()).toContain(repoPropsData.list[0].layers);
expect(tds.at(3).html()).toContain(repoPropsData.list[0].size);
expect(tds.at(4).html()).toContain(wrapper.vm.timeFormatted(repoPropsData.list[0].createdAt));
});
it('should have a label called Image ID', () => {
const label = findImageId();
expect(label.element).toMatchInlineSnapshot(`
<th>
Image ID
</th>
`);
});
});
describe('multi select', () => {
it('selecting a row should enable delete button', () => {
const deleteBtn = findDeleteButton();
const checkboxes = findSelectCheckboxes();
expect(deleteBtn.attributes('disabled')).toBe('disabled');
checkboxes.at(0).trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(deleteBtn.attributes('disabled')).toEqual(undefined);
});
});
it('selecting all checkbox should select all rows and enable delete button', () => {
const selectAll = findSelectAllCheckbox();
selectAll.trigger('click');
return wrapper.vm.$nextTick().then(() => {
const checkboxes = findSelectCheckboxes();
const checked = checkboxes.filter(w => w.element.checked);
expect(checked.length).toBe(checkboxes.length);
});
});
it('deselecting select all checkbox should deselect all rows and disable delete button', () => {
const checkboxes = findSelectCheckboxes();
const selectAll = findSelectAllCheckbox();
selectAll.trigger('click');
selectAll.trigger('click');
return wrapper.vm.$nextTick().then(() => {
const checked = checkboxes.filter(w => !w.element.checked);
expect(checked.length).toBe(checkboxes.length);
});
});
it('should delete multiple items when multiple items are selected', () => {
const multiDeleteItems = jest.fn().mockResolvedValue();
wrapper.setMethods({ multiDeleteItems });
return wrapper.vm
.$nextTick()
.then(() => {
const selectAll = findSelectAllCheckbox();
selectAll.trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
const deleteBtn = findDeleteButton();
expect(wrapper.vm.selectedItems).toEqual([0, 1]);
expect(deleteBtn.attributes('disabled')).toEqual(undefined);
wrapper.setData({ itemsToBeDeleted: [...wrapper.vm.selectedItems] });
wrapper.vm.handleMultipleDelete();
expect(wrapper.vm.selectedItems).toEqual([]);
expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
expect(wrapper.vm.multiDeleteItems).toHaveBeenCalledWith({
path: bulkDeletePath,
items: [firstImage.tag, secondImage.tag],
});
});
});
it('should show an error message if bulkDeletePath is not set', () => {
const showError = jest.fn();
wrapper.setMethods({ showError });
wrapper.setProps({
repo: {
...repoPropsData,
tagsPath: null,
},
});
wrapper.vm.handleMultipleDelete();
expect(createFlash).toHaveBeenCalled();
});
});
describe('delete registry', () => {
beforeEach(() => {
wrapper.setData({ selectedItems: [0] });
return wrapper.vm.$nextTick();
});
it('should be possible to delete a registry', () => {
const deleteBtn = findDeleteButton();
const deleteBtns = findDeleteButtonsRow();
expect(wrapper.vm.selectedItems).toEqual([0]);
expect(deleteBtn).toBeDefined();
expect(deleteBtn.attributes('disable')).toBe(undefined);
expect(deleteBtns.is('button')).toBe(true);
});
it('should allow deletion row by row', () => {
const deleteBtns = findDeleteButtonsRow();
const deleteSingleItem = jest.fn();
const deleteItem = jest.fn().mockResolvedValue();
wrapper.setMethods({ deleteSingleItem, deleteItem });
return wrapper.vm.$nextTick().then(() => {
deleteBtns.at(0).trigger('click');
expect(wrapper.vm.deleteSingleItem).toHaveBeenCalledWith(0);
wrapper.vm.handleSingleDelete(1);
expect(wrapper.vm.deleteItem).toHaveBeenCalledWith(1);
});
});
});
describe('modal event handlers', () => {
beforeEach(() => {
wrapper.vm.handleSingleDelete = jest.fn();
wrapper.vm.handleMultipleDelete = jest.fn();
});
it('on ok when one item is selected should call singleDelete', () => {
wrapper.setData({ itemsToBeDeleted: [0] });
wrapper.vm.onDeletionConfirmed();
expect(wrapper.vm.handleSingleDelete).toHaveBeenCalledWith(repoPropsData.list[0]);
expect(wrapper.vm.handleMultipleDelete).not.toHaveBeenCalled();
});
it('on ok when multiple items are selected should call multiDelete', () => {
wrapper.setData({ itemsToBeDeleted: [0, 1, 2] });
wrapper.vm.onDeletionConfirmed();
expect(wrapper.vm.handleMultipleDelete).toHaveBeenCalled();
expect(wrapper.vm.handleSingleDelete).not.toHaveBeenCalled();
});
});
describe('pagination', () => {
const repo = {
repoPropsData,
pagination: {
total: 20,
perPage: 2,
nextPage: 2,
},
};
beforeEach(() => {
wrapper = mount(tableRegistry, {
propsData: {
repo,
},
});
});
it('should exist', () => {
const pagination = findPagination();
expect(pagination.exists()).toBe(true);
});
it('should be visible when pagination is needed', () => {
const pagination = findPagination();
expect(pagination.isVisible()).toBe(true);
wrapper.setProps({
repo: {
pagination: {
total: 0,
perPage: 10,
},
},
});
expect(wrapper.vm.shouldRenderPagination).toBe(false);
});
it('should have a change function that update the list when run', () => {
const fetchList = jest.fn().mockResolvedValue();
wrapper.setMethods({ fetchList });
wrapper.vm.onPageChange(1);
expect(wrapper.vm.fetchList).toHaveBeenCalledWith({ repo, page: 1 });
});
});
describe('modal content', () => {
it('should show the singular title and image name when deleting a single image', () => {
wrapper.setData({ selectedItems: [1, 2, 3] });
wrapper.vm.deleteSingleItem(0);
expect(wrapper.vm.modalAction).toBe('Remove tag');
expect(wrapper.vm.modalDescription).toContain(firstImage.tag);
});
it('should show the plural title and image count when deleting more than one image', () => {
wrapper.setData({ selectedItems: [1, 2] });
wrapper.vm.deleteMultipleItems();
expect(wrapper.vm.modalAction).toBe('Remove tags');
expect(wrapper.vm.modalDescription).toContain('<b>2</b> tags');
});
});
describe('disabled delete', () => {
beforeEach(() => {
store = new Vuex.Store({
state: {
isDeleteDisabled: true,
},
getters,
});
wrapper = mountWithStore({
propsData: {
repo: repoPropsData,
canDeleteRepo: false,
},
});
});
it('should not render select all', () => {
const selectAll = findSelectAllCheckbox();
expect(selectAll.exists()).toBe(false);
});
it('should not render any select checkbox', () => {
const selects = findSelectCheckboxes();
expect(selects.length).toBe(0);
});
it('should not render delete registry button', () => {
const deleteBtn = findDeleteButton();
expect(deleteBtn.exists()).toBe(false);
});
it('should not render delete row button', () => {
const deleteBtns = findDeleteButtonsRow();
expect(deleteBtns.length).toBe(0);
});
});
describe('event tracking', () => {
const testTrackingCall = (action, label = 'registry_tag_delete') => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, action, { label, property: 'foo' });
};
beforeEach(() => {
jest.spyOn(Tracking, 'event');
wrapper.vm.handleSingleDelete = jest.fn();
wrapper.vm.handleMultipleDelete = jest.fn();
});
describe('single tag delete', () => {
beforeEach(() => {
wrapper.setData({ itemsToBeDeleted: [0] });
return wrapper.vm.$nextTick();
});
it('send an event when delete button is clicked', () => {
const deleteBtn = findDeleteButtonsRow();
deleteBtn.at(0).trigger('click');
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');
});
});
describe('bulk tag delete', () => {
beforeEach(() => {
const items = [0, 1, 2];
wrapper.setData({ itemsToBeDeleted: items, selectedItems: items });
return wrapper.vm.$nextTick();
});
it('send an event when delete button is clicked', () => {
const deleteBtn = findDeleteButton();
deleteBtn.vm.$emit('click');
testTrackingCall('click_button', 'bulk_registry_tag_delete');
});
it('send an event when cancel is pressed on modal', () => {
const deleteModal = findDeleteModal();
deleteModal.vm.$emit('cancel');
testTrackingCall('cancel_delete', 'bulk_registry_tag_delete');
});
it('send an event when confirm is clicked on modal', () => {
const deleteModal = findDeleteModal();
deleteModal.vm.$emit('ok');
testTrackingCall('confirm_delete', 'bulk_registry_tag_delete');
});
});
});
});