diff options
Diffstat (limited to 'spec/frontend/boards/components')
12 files changed, 221 insertions, 147 deletions
diff --git a/spec/frontend/boards/components/board_add_new_column_trigger_spec.js b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js index c35f2463f69..7dd02bf1d35 100644 --- a/spec/frontend/boards/components/board_add_new_column_trigger_spec.js +++ b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js @@ -1,5 +1,5 @@ import { GlButton } from '@gitlab/ui'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue'; @@ -49,7 +49,7 @@ describe('BoardAddNewColumnTrigger', () => { it('shows the tooltip', async () => { wrapper.find(GlButton).vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); const tooltip = findTooltipText(); diff --git a/spec/frontend/boards/components/board_blocked_icon_spec.js b/spec/frontend/boards/components/board_blocked_icon_spec.js index 7b04942f056..7a5c49bd488 100644 --- a/spec/frontend/boards/components/board_blocked_icon_spec.js +++ b/spec/frontend/boards/components/board_blocked_icon_spec.js @@ -1,6 +1,6 @@ import { GlIcon, GlLink, GlPopover, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount, mount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; @@ -39,7 +39,7 @@ describe('BoardBlockedIcon', () => { const mouseenter = async () => { findGlIcon().vm.$emit('mouseenter'); - await wrapper.vm.$nextTick(); + await nextTick(); await waitForApollo(); }; diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js index 3af173aa18c..aad89cf8261 100644 --- a/spec/frontend/boards/components/board_card_spec.js +++ b/spec/frontend/boards/components/board_card_spec.js @@ -1,6 +1,6 @@ import { GlLabel } from '@gitlab/ui'; import { shallowMount, mount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import BoardCard from '~/boards/components/board_card.vue'; @@ -65,12 +65,12 @@ describe('Board card', () => { const selectCard = async () => { wrapper.trigger('click'); - await wrapper.vm.$nextTick(); + await nextTick(); }; const multiSelectCard = async () => { wrapper.trigger('click', { ctrlKey: true }); - await wrapper.vm.$nextTick(); + await nextTick(); }; beforeEach(() => { diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js index a8398a138ba..85ba703a6ee 100644 --- a/spec/frontend/boards/components/board_filtered_search_spec.js +++ b/spec/frontend/boards/components/board_filtered_search_spec.js @@ -120,7 +120,7 @@ describe('BoardFilteredSearch', () => { { type: 'author', value: { data: 'root', operator: '=' } }, { type: 'assignee', value: { data: 'root', operator: '=' } }, { type: 'label', value: { data: 'label', operator: '=' } }, - { type: 'label', value: { data: 'label2', operator: '=' } }, + { type: 'label', value: { data: 'label&2', operator: '=' } }, { type: 'milestone', value: { data: 'New Milestone', operator: '=' } }, { type: 'type', value: { data: 'INCIDENT', operator: '=' } }, { type: 'weight', value: { data: '2', operator: '=' } }, @@ -134,7 +134,7 @@ describe('BoardFilteredSearch', () => { title: '', replace: true, url: - 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&assignee_username=root&milestone_title=New+Milestone&iteration_id=3341&types=INCIDENT&weight=2&release_tag=v1.0.0', + 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label%262&assignee_username=root&milestone_title=New%20Milestone&iteration_id=3341&types=INCIDENT&weight=2&release_tag=v1.0.0', }); }); diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js index 692fd3ec555..5678da2a246 100644 --- a/spec/frontend/boards/components/board_form_spec.js +++ b/spec/frontend/boards/components/board_form_spec.js @@ -130,7 +130,7 @@ describe('BoardForm', () => { it('passes correct primary action text and variant', () => { expect(findModalActionPrimary().text).toBe('Create board'); - expect(findModalActionPrimary().attributes[0].variant).toBe('success'); + expect(findModalActionPrimary().attributes[0].variant).toBe('confirm'); }); it('does not render delete confirmation message', () => { diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js index 8cc0ad5f30c..14870ec76a2 100644 --- a/spec/frontend/boards/components/board_list_header_spec.js +++ b/spec/frontend/boards/components/board_list_header_spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import Vuex from 'vuex'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -148,7 +148,7 @@ describe('Board List Header Component', () => { findCaret().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(toggleListCollapsedSpy).toHaveBeenCalledTimes(1); }); @@ -156,7 +156,7 @@ describe('Board List Header Component', () => { createComponent({ withLocalStorage: false, currentUserId: 1 }); findCaret().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(updateListSpy).toHaveBeenCalledTimes(1); expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(null); @@ -168,7 +168,7 @@ describe('Board List Header Component', () => { }); findCaret().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(updateListSpy).not.toHaveBeenCalled(); expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.collapsed`)).toBe(String(isCollapsed())); diff --git a/spec/frontend/boards/components/board_new_issue_spec.js b/spec/frontend/boards/components/board_new_issue_spec.js index 57ccebf3676..8b0100d069a 100644 --- a/spec/frontend/boards/components/board_new_issue_spec.js +++ b/spec/frontend/boards/components/board_new_issue_spec.js @@ -1,15 +1,14 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import BoardNewIssue from '~/boards/components/board_new_issue.vue'; import BoardNewItem from '~/boards/components/board_new_item.vue'; import ProjectSelect from '~/boards/components/project_select.vue'; import eventHub from '~/boards/eventhub'; -import { mockList, mockGroupProjects } from '../mock_data'; +import { mockList, mockGroupProjects, mockIssue, mockIssue2 } from '../mock_data'; -const localVue = createLocalVue(); - -localVue.use(Vuex); +Vue.use(Vuex); const addListNewIssuesSpy = jest.fn().mockResolvedValue(); const mockActions = { addListNewIssue: addListNewIssuesSpy }; @@ -17,10 +16,9 @@ const mockActions = { addListNewIssue: addListNewIssuesSpy }; const createComponent = ({ state = { selectedProject: mockGroupProjects[0], fullPath: mockGroupProjects[0].fullPath }, actions = mockActions, - getters = { isGroupBoard: () => true, isProjectBoard: () => false }, + getters = { isGroupBoard: () => true, getBoardItemsByList: () => () => [] }, } = {}) => shallowMount(BoardNewIssue, { - localVue, store: new Vuex.Store({ state, actions, @@ -47,7 +45,7 @@ describe('Issue boards new issue form', () => { beforeEach(async () => { wrapper = createComponent(); - await wrapper.vm.$nextTick(); + await nextTick(); }); afterEach(() => { @@ -68,7 +66,7 @@ describe('Issue boards new issue form', () => { it('calls addListNewIssue action when `board-new-item` emits form-submit event', async () => { findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' }); - await wrapper.vm.$nextTick(); + await nextTick(); expect(addListNewIssuesSpy).toHaveBeenCalledWith(expect.any(Object), { list: mockList, issueInput: { @@ -77,15 +75,44 @@ describe('Issue boards new issue form', () => { assigneeIds: [], milestoneId: undefined, projectPath: mockGroupProjects[0].fullPath, + moveAfterId: undefined, }, }); }); + describe('when list has an existing issues', () => { + beforeEach(() => { + wrapper = createComponent({ + getters: { + isGroupBoard: () => true, + getBoardItemsByList: () => () => [mockIssue, mockIssue2], + }, + }); + }); + + it('it uses the first issue ID as moveAfterId', async () => { + findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' }); + + await nextTick(); + expect(addListNewIssuesSpy).toHaveBeenCalledWith(expect.any(Object), { + list: mockList, + issueInput: { + title: 'Foo', + labelIds: [], + assigneeIds: [], + milestoneId: undefined, + projectPath: mockGroupProjects[0].fullPath, + moveAfterId: mockIssue.id, + }, + }); + }); + }); + it('emits event `toggle-issue-form` with current list Id suffix on eventHub when `board-new-item` emits form-cancel event', async () => { jest.spyOn(eventHub, '$emit').mockImplementation(); findBoardNewItem().vm.$emit('form-cancel'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(eventHub.$emit).toHaveBeenCalledWith(`toggle-issue-form-${mockList.id}`); }); @@ -101,7 +128,7 @@ describe('Issue boards new issue form', () => { describe('when in project issue board', () => { beforeEach(() => { wrapper = createComponent({ - getters: { isGroupBoard: () => false, isProjectBoard: () => true }, + getters: { isGroupBoard: () => false }, }); }); diff --git a/spec/frontend/boards/components/board_new_item_spec.js b/spec/frontend/boards/components/board_new_item_spec.js index 0151d9c1c14..86cebc8a719 100644 --- a/spec/frontend/boards/components/board_new_item_spec.js +++ b/spec/frontend/boards/components/board_new_item_spec.js @@ -1,4 +1,5 @@ import { GlForm, GlFormInput, GlButton } from '@gitlab/ui'; +import { nextTick } from 'vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import BoardNewItem from '~/boards/components/board_new_item.vue'; @@ -39,6 +40,27 @@ describe('BoardNewItem', () => { }); describe('template', () => { + describe('when the user provides a valid input', () => { + it('finds an enabled create button', async () => { + expect(wrapper.findByTestId('create-button').props('disabled')).toBe(true); + + wrapper.find(GlFormInput).vm.$emit('input', 'hello'); + await nextTick(); + + expect(wrapper.findByTestId('create-button').props('disabled')).toBe(false); + }); + }); + + describe('when the user types in a string with only spaces', () => { + it('disables the Create Issue button', async () => { + wrapper.find(GlFormInput).vm.$emit('input', ' '); + + await nextTick(); + + expect(wrapper.findByTestId('create-button').props('disabled')).toBe(true); + }); + }); + it('renders gl-form component', () => { expect(wrapper.findComponent(GlForm).exists()).toBe(true); }); @@ -80,6 +102,19 @@ describe('BoardNewItem', () => { ]); }); + it('emits `form-submit` event with trimmed title', async () => { + titleInput().setValue(' Foo '); + + await glForm().trigger('submit'); + + expect(wrapper.emitted('form-submit')[0]).toEqual([ + { + title: 'Foo', + list: mockList, + }, + ]); + }); + it('emits `scroll-board-list-` event with list.id on eventHub when `submit` is triggered on gl-form', async () => { jest.spyOn(eventHub, '$emit').mockImplementation(); await glForm().trigger('submit'); @@ -90,7 +125,7 @@ describe('BoardNewItem', () => { it('emits `form-cancel` event and clears title value when `reset` is triggered on gl-form', async () => { titleInput().setValue('Foo'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(titleInput().element.value).toBe('Foo'); await glForm().trigger('reset'); diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js index 46dd109ffb1..7f40c426b30 100644 --- a/spec/frontend/boards/components/board_settings_sidebar_spec.js +++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js @@ -1,8 +1,9 @@ -import { GlDrawer, GlLabel } from '@gitlab/ui'; +import { GlDrawer, GlLabel, GlModal, GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { MountingPortal } from 'portal-vue'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { stubComponent } from 'helpers/stub_component'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; @@ -20,8 +21,7 @@ describe('BoardSettingsSidebar', () => { const labelTitle = mockLabelList.label.title; const labelColor = mockLabelList.label.color; const listId = mockLabelList.id; - - const findRemoveButton = () => wrapper.findByTestId('remove-list'); + const modalID = 'board-settings-sidebar-modal'; const createComponent = ({ canAdminList = false, @@ -46,6 +46,9 @@ describe('BoardSettingsSidebar', () => { canAdminList, scopedLabelsAvailable: false, }, + directives: { + GlModal: createMockDirective(), + }, stubs: { GlDrawer: stubComponent(GlDrawer, { template: '<div><slot name="header"></slot><slot></slot></div>', @@ -56,6 +59,8 @@ describe('BoardSettingsSidebar', () => { }; const findLabel = () => wrapper.find(GlLabel); const findDrawer = () => wrapper.find(GlDrawer); + const findModal = () => wrapper.find(GlModal); + const findRemoveButton = () => wrapper.find(GlButton); afterEach(() => { jest.restoreAllMocks(); @@ -86,7 +91,7 @@ describe('BoardSettingsSidebar', () => { findDrawer().vm.$emit('close'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.find(GlDrawer).exists()).toBe(false); }); @@ -96,7 +101,7 @@ describe('BoardSettingsSidebar', () => { sidebarEventHub.$emit('sidebar.closeAll'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.find(GlDrawer).exists()).toBe(false); }); @@ -161,5 +166,16 @@ describe('BoardSettingsSidebar', () => { expect(findRemoveButton().exists()).toBe(true); }); + + it('has the correct ID on the button', () => { + createComponent({ canAdminList: true, activeId: listId, list: mockLabelList }); + const binding = getBinding(findRemoveButton().element, 'gl-modal'); + expect(binding.value).toBe(modalID); + }); + + it('has the correct ID on the modal', () => { + createComponent({ canAdminList: true, activeId: listId, list: mockLabelList }); + expect(findModal().props('modalId')).toBe(modalID); + }); }); }); diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js index 9cf7c5774bf..26a5bf34595 100644 --- a/spec/frontend/boards/components/boards_selector_spec.js +++ b/spec/frontend/boards/components/boards_selector_spec.js @@ -1,43 +1,40 @@ import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; -import MockAdapter from 'axios-mock-adapter'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import Vuex from 'vuex'; import { TEST_HOST } from 'spec/test_constants'; import BoardsSelector from '~/boards/components/boards_selector.vue'; +import { BoardType } from '~/boards/constants'; import groupBoardQuery from '~/boards/graphql/group_board.query.graphql'; import projectBoardQuery from '~/boards/graphql/project_board.query.graphql'; +import groupBoardsQuery from '~/boards/graphql/group_boards.query.graphql'; +import projectBoardsQuery from '~/boards/graphql/project_boards.query.graphql'; +import groupRecentBoardsQuery from '~/boards/graphql/group_recent_boards.query.graphql'; +import projectRecentBoardsQuery from '~/boards/graphql/project_recent_boards.query.graphql'; import defaultStore from '~/boards/stores'; -import axios from '~/lib/utils/axios_utils'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { mockGroupBoardResponse, mockProjectBoardResponse } from '../mock_data'; +import { + mockGroupBoardResponse, + mockProjectBoardResponse, + mockGroupAllBoardsResponse, + mockProjectAllBoardsResponse, + mockGroupRecentBoardsResponse, + mockProjectRecentBoardsResponse, + mockSmallProjectAllBoardsResponse, + mockEmptyProjectRecentBoardsResponse, + boards, + recentIssueBoards, +} from '../mock_data'; const throttleDuration = 1; Vue.use(VueApollo); -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; - let mock; let fakeApollo; let store; - const boards = boardGenerator(20); - const recentBoards = boardGenerator(5); const createStore = ({ isGroupBoard = false, isProjectBoard = false } = {}) => { store = new Vuex.Store({ @@ -63,17 +60,43 @@ describe('BoardsSelector', () => { }; const getDropdownItems = () => wrapper.findAll('.js-dropdown-item'); - const getDropdownHeaders = () => wrapper.findAll(GlDropdownSectionHeader); - const getLoadingIcon = () => wrapper.find(GlLoadingIcon); - const findDropdown = () => wrapper.find(GlDropdown); + const getDropdownHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader); + const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findDropdown = () => wrapper.findComponent(GlDropdown); const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse); const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse); - const createComponent = () => { + const projectBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockProjectAllBoardsResponse); + const groupBoardsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupAllBoardsResponse); + + const projectRecentBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockProjectRecentBoardsResponse); + const groupRecentBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockGroupRecentBoardsResponse); + + const smallBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockSmallProjectAllBoardsResponse); + const emptyRecentBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockEmptyProjectRecentBoardsResponse); + + const createComponent = ({ + projectBoardsQueryHandler = projectBoardsQueryHandlerSuccess, + projectRecentBoardsQueryHandler = projectRecentBoardsQueryHandlerSuccess, + } = {}) => { fakeApollo = createMockApollo([ [projectBoardQuery, projectBoardQueryHandlerSuccess], [groupBoardQuery, groupBoardQueryHandlerSuccess], + [projectBoardsQuery, projectBoardsQueryHandler], + [groupBoardsQuery, groupBoardsQueryHandlerSuccess], + [projectRecentBoardsQuery, projectRecentBoardsQueryHandler], + [groupRecentBoardsQuery, groupRecentBoardsQueryHandlerSuccess], ]); wrapper = mount(BoardsSelector, { @@ -91,67 +114,34 @@ describe('BoardsSelector', () => { attachTo: document.body, provide: { fullPath: '', - recentBoardsEndpoint: `${TEST_HOST}/recent`, }, }); - - wrapper.vm.$apollo.addSmartQuery = jest.fn((_, options) => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - [options.loadingKey]: true, - }); - }); }; afterEach(() => { wrapper.destroy(); - wrapper = null; - mock.restore(); + fakeApollo = null; }); - describe('fetching all boards', () => { + describe('template', () => { beforeEach(() => { - mock = new MockAdapter(axios); - - allBoardsResponse = Promise.resolve({ - data: { - group: { - boards: { - edges: boards.map((board) => ({ node: board })), - }, - }, - }, - }); - recentBoardsResponse = Promise.resolve({ - data: recentBoards, - }); - - createStore(); + createStore({ isProjectBoard: true }); createComponent(); - - mock.onGet(`${TEST_HOST}/recent`).replyOnce(200, recentBoards); }); describe('loading', () => { - beforeEach(async () => { - // Wait for current board to be loaded - await nextTick(); - - // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time - findDropdown().vm.$emit('show'); - }); - // we are testing loading state, so don't resolve responses until after the tests afterEach(async () => { - await Promise.all([allBoardsResponse, recentBoardsResponse]); await nextTick(); }); it('shows loading spinner', () => { + // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time + findDropdown().vm.$emit('show'); + + expect(getLoadingIcon().exists()).toBe(true); expect(getDropdownHeaders()).toHaveLength(0); expect(getDropdownItems()).toHaveLength(0); - expect(getLoadingIcon().exists()).toBe(true); }); }); @@ -163,16 +153,13 @@ describe('BoardsSelector', () => { // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time findDropdown().vm.$emit('show'); - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - await wrapper.setData({ - loadingBoards: false, - loadingRecentBoards: false, - }); - await Promise.all([allBoardsResponse, recentBoardsResponse]); await nextTick(); }); + it('fetches all issue boards', () => { + expect(projectBoardsQueryHandlerSuccess).toHaveBeenCalled(); + }); + it('hides loading spinner', async () => { await nextTick(); expect(getLoadingIcon().exists()).toBe(false); @@ -180,22 +167,17 @@ describe('BoardsSelector', () => { describe('filtering', () => { beforeEach(async () => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - boards, - }); - await nextTick(); }); it('shows all boards without filtering', () => { - expect(getDropdownItems()).toHaveLength(boards.length + recentBoards.length); + expect(getDropdownItems()).toHaveLength(boards.length + recentIssueBoards.length); }); it('shows only matching boards when filtering', async () => { const filterTerm = 'board1'; - const expectedCount = boards.filter((board) => board.name.includes(filterTerm)).length; + const expectedCount = boards.filter((board) => board.node.name.includes(filterTerm)) + .length; fillSearchBox(filterTerm); @@ -214,32 +196,21 @@ describe('BoardsSelector', () => { describe('recent boards section', () => { it('shows only when boards are greater than 10', async () => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - boards, - }); - await nextTick(); + expect(projectRecentBoardsQueryHandlerSuccess).toHaveBeenCalled(); expect(getDropdownHeaders()).toHaveLength(2); }); it('does not show when boards are less than 10', async () => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - boards: boards.slice(0, 5), - }); + createComponent({ projectBoardsQueryHandler: smallBoardsQueryHandlerSuccess }); await nextTick(); expect(getDropdownHeaders()).toHaveLength(0); }); - it('does not show when recentBoards api returns empty array', async () => { - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - recentBoards: [], + it('does not show when recentIssueBoards api returns empty array', async () => { + createComponent({ + projectRecentBoardsQueryHandler: emptyRecentBoardsQueryHandlerSuccess, }); await nextTick(); @@ -256,15 +227,39 @@ describe('BoardsSelector', () => { }); }); + describe('fetching all boards', () => { + it.each` + boardType | queryHandler | notCalledHandler + ${BoardType.group} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess} + ${BoardType.project} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess} + `('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => { + createStore({ + isProjectBoard: boardType === BoardType.project, + isGroupBoard: boardType === BoardType.group, + }); + createComponent(); + + await nextTick(); + + // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time + findDropdown().vm.$emit('show'); + + await nextTick(); + + expect(queryHandler).toHaveBeenCalled(); + expect(notCalledHandler).not.toHaveBeenCalled(); + }); + }); + describe('fetching current board', () => { it.each` - boardType | queryHandler | notCalledHandler - ${'group'} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess} - ${'project'} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess} + boardType | queryHandler | notCalledHandler + ${BoardType.group} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess} + ${BoardType.project} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess} `('fetches $boardType board', async ({ boardType, queryHandler, notCalledHandler }) => { createStore({ - isProjectBoard: boardType === 'project', - isGroupBoard: boardType === 'group', + isProjectBoard: boardType === BoardType.project, + isGroupBoard: boardType === BoardType.group, }); createComponent(); diff --git a/spec/frontend/boards/components/sidebar/board_editable_item_spec.js b/spec/frontend/boards/components/sidebar/board_editable_item_spec.js index 12e9a9ba365..0c76c711b3a 100644 --- a/spec/frontend/boards/components/sidebar/board_editable_item_spec.js +++ b/spec/frontend/boards/components/sidebar/board_editable_item_spec.js @@ -1,5 +1,6 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import BoardSidebarItem from '~/boards/components/sidebar/board_editable_item.vue'; describe('boards sidebar remove issue', () => { @@ -79,17 +80,16 @@ describe('boards sidebar remove issue', () => { createComponent({ canUpdate: true, slots }); findEditButton().vm.$emit('click'); - return wrapper.vm.$nextTick().then(() => { - expect(findCollapsed().isVisible()).toBe(false); - expect(findExpanded().isVisible()).toBe(true); - }); + await nextTick(); + expect(findCollapsed().isVisible()).toBe(false); + expect(findExpanded().isVisible()).toBe(true); }); it('hides the header while editing if `toggleHeader` is true', async () => { createComponent({ canUpdate: true, props: { toggleHeader: true } }); findEditButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(findEditButton().isVisible()).toBe(false); expect(findTitle().isVisible()).toBe(false); @@ -101,14 +101,14 @@ describe('boards sidebar remove issue', () => { beforeEach(async () => { createComponent({ canUpdate: true }); findEditButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('hides expanded section and displays collapsed section', async () => { expect(findExpanded().isVisible()).toBe(true); document.body.click(); - await wrapper.vm.$nextTick(); + await nextTick(); expect(findCollapsed().isVisible()).toBe(true); expect(findExpanded().isVisible()).toBe(false); @@ -117,7 +117,7 @@ describe('boards sidebar remove issue', () => { it('emits events', async () => { document.body.click(); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.emitted().close).toHaveLength(1); expect(wrapper.emitted()['off-click']).toHaveLength(1); @@ -129,7 +129,7 @@ describe('boards sidebar remove issue', () => { findEditButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); expect(wrapper.emitted().open.length).toBe(1); }); @@ -139,7 +139,7 @@ describe('boards sidebar remove issue', () => { findEditButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); wrapper.vm.collapse({ emitEvent: false }); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js index 4a8eda298f2..5364d929c38 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_title_spec.js @@ -1,5 +1,6 @@ import { GlAlert, GlFormInput, GlForm } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue'; import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue'; import { createStore } from '~/boards/stores'; @@ -75,7 +76,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => { }); findFormInput().vm.$emit('input', TEST_TITLE); findForm().vm.$emit('submit', { preventDefault: () => {} }); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('collapses sidebar and renders new title', () => { @@ -98,7 +99,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => { jest.spyOn(wrapper.vm, 'setActiveItemTitle').mockImplementation(() => {}); findFormInput().vm.$emit('input', ''); findForm().vm.$emit('submit', { preventDefault: () => {} }); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('commits change to the server', () => { @@ -113,7 +114,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => { wrapper.vm.$refs.sidebarItem.expand(); findFormInput().vm.$emit('input', TEST_TITLE); findEditableItem().vm.$emit('off-click'); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('does not collapses sidebar and shows alert', () => { @@ -148,7 +149,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => { }); findFormInput().vm.$emit('input', TEST_TITLE); findCancelButton().vm.$emit('click'); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('collapses sidebar and render former title', () => { @@ -168,7 +169,7 @@ describe('~/boards/components/sidebar/board_sidebar_title.vue', () => { jest.spyOn(wrapper.vm, 'setError').mockImplementation(() => {}); findFormInput().vm.$emit('input', 'Invalid title'); findForm().vm.$emit('submit', { preventDefault: () => {} }); - await wrapper.vm.$nextTick(); + await nextTick(); }); it('collapses sidebar and renders former item title', () => { |