import Vue from 'vue'; import Vuex from 'vuex'; import tableRegistry from '~/registry/components/table_registry.vue'; import { mount, createLocalVue } from '@vue/test-utils'; import { repoPropsData } from '../mock_data'; import * as getters from '~/registry/stores/getters'; const [firstImage, secondImage] = repoPropsData.list; const localVue = createLocalVue(); localVue.use(Vuex); describe('table registry', () => { let wrapper; let store; const findSelectAllCheckbox = w => w.find('.js-select-all-checkbox > input'); const findSelectCheckboxes = w => w.findAll('.js-select-checkbox > input'); const findDeleteButton = w => w.find('.js-delete-registry'); const findDeleteButtonsRow = w => w.findAll('.js-delete-registry-row'); const findPagination = w => w.find('.js-registry-pagination'); const bulkDeletePath = 'path'; const mountWithStore = config => mount(tableRegistry, { ...config, store, localVue }); beforeEach(() => { // This is needed due to console.error called by vue to emit a warning that stop the tests // see https://github.com/vuejs/vue-test-utils/issues/532 Vue.config.silent = true; store = new Vuex.Store({ state: { isDeleteDisabled: false, }, getters, }); wrapper = mountWithStore({ propsData: { repo: repoPropsData, canDeleteRepo: true, }, }); }); afterEach(() => { Vue.config.silent = false; 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.timeFormated(repoPropsData.list[0].createdAt)); }); }); describe('multi select', () => { it('selecting a row should enable delete button', done => { const deleteBtn = findDeleteButton(wrapper); const checkboxes = findSelectCheckboxes(wrapper); expect(deleteBtn.attributes('disabled')).toBe('disabled'); checkboxes.at(0).trigger('click'); Vue.nextTick(() => { expect(deleteBtn.attributes('disabled')).toEqual(undefined); done(); }); }); it('selecting all checkbox should select all rows and enable delete button', done => { const selectAll = findSelectAllCheckbox(wrapper); const checkboxes = findSelectCheckboxes(wrapper); selectAll.trigger('click'); Vue.nextTick(() => { const checked = checkboxes.filter(w => w.element.checked); expect(checked.length).toBe(checkboxes.length); done(); }); }); it('deselecting select all checkbox should deselect all rows and disable delete button', done => { const checkboxes = findSelectCheckboxes(wrapper); const selectAll = findSelectAllCheckbox(wrapper); selectAll.trigger('click'); selectAll.trigger('click'); Vue.nextTick(() => { const checked = checkboxes.filter(w => !w.element.checked); expect(checked.length).toBe(checkboxes.length); done(); }); }); it('should delete multiple items when multiple items are selected', done => { const multiDeleteItems = jest.fn().mockResolvedValue(); wrapper.setMethods({ multiDeleteItems }); const selectAll = findSelectAllCheckbox(wrapper); selectAll.trigger('click'); Vue.nextTick(() => { const deleteBtn = findDeleteButton(wrapper); expect(wrapper.vm.selectedItems).toEqual([0, 1]); expect(deleteBtn.attributes('disabled')).toEqual(undefined); wrapper.setData({ itemsToBeDeleted: [...wrapper.vm.selectedItems] }); wrapper.vm.handleMultipleDelete(); Vue.nextTick(() => { expect(wrapper.vm.selectedItems).toEqual([]); expect(wrapper.vm.itemsToBeDeleted).toEqual([]); expect(wrapper.vm.multiDeleteItems).toHaveBeenCalledWith({ path: bulkDeletePath, items: [firstImage.tag, secondImage.tag], }); done(); }); }); }); 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(wrapper.vm.showError).toHaveBeenCalled(); }); }); describe('delete registry', () => { beforeEach(() => { wrapper.setData({ selectedItems: [0] }); }); it('should be possible to delete a registry', () => { const deleteBtn = findDeleteButton(wrapper); const deleteBtns = findDeleteButtonsRow(wrapper); 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(wrapper); const deleteSingleItem = jest.fn(); const deleteItem = jest.fn().mockResolvedValue(); wrapper.setMethods({ deleteSingleItem, deleteItem }); deleteBtns.at(0).trigger('click'); expect(wrapper.vm.deleteSingleItem).toHaveBeenCalledWith(0); wrapper.vm.handleSingleDelete(1); expect(wrapper.vm.deleteItem).toHaveBeenCalledWith(1); }); }); describe('pagination', () => { const repo = { repoPropsData, pagination: { total: 20, perPage: 2, nextPage: 2, }, }; beforeEach(() => { wrapper = mount(tableRegistry, { propsData: { repo, }, }); }); it('should exist', () => { const pagination = findPagination(wrapper); expect(pagination.exists()).toBe(true); }); it('should be visible when pagination is needed', () => { const pagination = findPagination(wrapper); 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('2 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(wrapper); expect(selectAll.exists()).toBe(false); }); it('should not render any select checkbox', () => { const selects = findSelectCheckboxes(wrapper); expect(selects.length).toBe(0); }); it('should not render delete registry button', () => { const deleteBtn = findDeleteButton(wrapper); expect(deleteBtn.exists()).toBe(false); }); it('should not render delete row button', () => { const deleteBtns = findDeleteButtonsRow(wrapper); expect(deleteBtns.length).toBe(0); }); }); });