import { nextTick } from 'vue'; import { mount } from '@vue/test-utils'; import { GlDropdown, GlLoadingIcon } from '@gitlab/ui'; import { TEST_HOST } from 'spec/test_constants'; import BoardsSelector from '~/boards/components/boards_selector.vue'; import boardsStore from '~/boards/stores/boards_store'; const throttleDuration = 1; function boardGenerator(n) { return new Array(n).fill().map((board, index) => { const id = `${index}`; const name = `board${id}`; return { id, name, }; }); } describe('BoardsSelector', () => { let wrapper; let allBoardsResponse; let recentBoardsResponse; const boards = boardGenerator(20); const recentBoards = boardGenerator(5); const fillSearchBox = filterTerm => { const searchBox = wrapper.find({ ref: 'searchBox' }); const searchBoxInput = searchBox.find('input'); searchBoxInput.setValue(filterTerm); searchBoxInput.trigger('input'); }; const getDropdownItems = () => wrapper.findAll('.js-dropdown-item'); const getDropdownHeaders = () => wrapper.findAll('.dropdown-bold-header'); const getLoadingIcon = () => wrapper.find(GlLoadingIcon); beforeEach(() => { const $apollo = { queries: { boards: { loading: false, }, }, }; boardsStore.setEndpoints({ boardsEndpoint: '', recentBoardsEndpoint: '', listsEndpoint: '', bulkUpdatePath: '', boardId: '', }); allBoardsResponse = Promise.resolve({ data: { group: { boards: { edges: boards.map(board => ({ node: board })), }, }, }, }); recentBoardsResponse = Promise.resolve({ data: recentBoards, }); boardsStore.allBoards = jest.fn(() => allBoardsResponse); boardsStore.recentBoards = jest.fn(() => recentBoardsResponse); wrapper = mount(BoardsSelector, { propsData: { throttleDuration, currentBoard: { id: 1, name: 'Development', milestone_id: null, weight: null, assignee_id: null, labels: [], }, milestonePath: `${TEST_HOST}/milestone/path`, boardBaseUrl: `${TEST_HOST}/board/base/url`, hasMissingBoards: false, canAdminBoard: true, multipleIssueBoardsAvailable: true, labelsPath: `${TEST_HOST}/labels/path`, projectId: 42, groupId: 19, scopedIssueBoardFeatureEnabled: true, weights: [], }, mocks: { $apollo }, attachToDocument: true, }); wrapper.vm.$apollo.addSmartQuery = jest.fn((_, options) => { wrapper.setData({ [options.loadingKey]: true, }); }); // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time wrapper.find(GlDropdown).vm.$emit('show'); }); afterEach(() => { wrapper.destroy(); wrapper = null; }); describe('loading', () => { // we are testing loading state, so don't resolve responses until after the tests afterEach(() => { return Promise.all([allBoardsResponse, recentBoardsResponse]).then(() => nextTick()); }); it('shows loading spinner', () => { expect(getDropdownHeaders()).toHaveLength(0); expect(getDropdownItems()).toHaveLength(0); expect(getLoadingIcon().exists()).toBe(true); }); }); describe('loaded', () => { beforeEach(() => { return Promise.all([allBoardsResponse, recentBoardsResponse]).then(() => nextTick()); }); it('hides loading spinner', () => { expect(getLoadingIcon().exists()).toBe(false); }); describe('filtering', () => { beforeEach(() => { wrapper.setData({ boards, }); return nextTick(); }); it('shows all boards without filtering', () => { expect(getDropdownItems()).toHaveLength(boards.length + recentBoards.length); }); it('shows only matching boards when filtering', () => { const filterTerm = 'board1'; const expectedCount = boards.filter(board => board.name.includes(filterTerm)).length; fillSearchBox(filterTerm); return nextTick().then(() => { expect(getDropdownItems()).toHaveLength(expectedCount); }); }); it('shows message if there are no matching boards', () => { fillSearchBox('does not exist'); return nextTick().then(() => { expect(getDropdownItems()).toHaveLength(0); expect(wrapper.text().includes('No matching boards found')).toBe(true); }); }); }); describe('recent boards section', () => { it('shows only when boards are greater than 10', () => { wrapper.setData({ boards, }); return nextTick().then(() => { expect(getDropdownHeaders()).toHaveLength(2); }); }); it('does not show when boards are less than 10', () => { wrapper.setData({ boards: boards.slice(0, 5), }); return nextTick().then(() => { expect(getDropdownHeaders()).toHaveLength(0); }); }); it('does not show when recentBoards api returns empty array', () => { wrapper.setData({ recentBoards: [], }); return nextTick().then(() => { expect(getDropdownHeaders()).toHaveLength(0); }); }); it('does not show when search is active', () => { fillSearchBox('Random string'); return nextTick().then(() => { expect(getDropdownHeaders()).toHaveLength(0); }); }); }); }); });