From 6be6c7761d35bca8fa08a69d146469a6c82aa247 Mon Sep 17 00:00:00 2001 From: Florie Guibert <fguibert@gitlab.com> Date: Thu, 10 Feb 2022 15:34:03 +1000 Subject: [PATCH] Issue boards - Fetch recent board in GraphQL No user facing changes --- .../boards/components/boards_selector.vue | 54 ++--- .../graphql/group_recent_boards.query.graphql | 14 ++ .../project_recent_boards.query.graphql | 14 ++ app/assets/javascripts/boards/index.js | 1 - .../boards/mount_multiple_boards_switcher.js | 1 - .../boards/components/boards_selector.vue | 3 +- .../boards/components/boards_selector_spec.js | 147 +++++--------- ee/spec/frontend/boards/mock_data.js | 21 ++ .../boards/components/boards_selector_spec.js | 188 +++++++++--------- spec/frontend/boards/mock_data.js | 78 ++++++++ 10 files changed, 287 insertions(+), 234 deletions(-) create mode 100644 app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql create mode 100644 app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index 69343cd78d8d3..6dbb1ea0050d2 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -14,8 +14,6 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import BoardForm from 'ee_else_ce/boards/components/board_form.vue'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import axios from '~/lib/utils/axios_utils'; -import httpStatusCodes from '~/lib/utils/http_status'; import { s__ } from '~/locale'; import eventHub from '../eventhub'; @@ -23,6 +21,8 @@ import groupBoardsQuery from '../graphql/group_boards.query.graphql'; import projectBoardsQuery from '../graphql/project_boards.query.graphql'; import groupBoardQuery from '../graphql/group_board.query.graphql'; import projectBoardQuery from '../graphql/project_board.query.graphql'; +import groupRecentBoardsQuery from '../graphql/group_recent_boards.query.graphql'; +import projectRecentBoardsQuery from '../graphql/project_recent_boards.query.graphql'; const MIN_BOARDS_TO_VIEW_RECENT = 10; @@ -40,7 +40,7 @@ export default { directives: { GlModalDirective, }, - inject: ['fullPath', 'recentBoardsEndpoint'], + inject: ['fullPath'], props: { throttleDuration: { type: Number, @@ -158,6 +158,10 @@ export default { this.scrollFadeInitialized = false; this.$nextTick(this.setScrollFade); }, + recentBoards() { + this.scrollFadeInitialized = false; + this.$nextTick(this.setScrollFade); + }, }, created() { eventHub.$on('showBoardModal', this.showPage); @@ -173,11 +177,11 @@ export default { cancel() { this.showPage(''); }, - boardUpdate(data) { + boardUpdate(data, boardType) { if (!data?.[this.parentType]) { return []; } - return data[this.parentType].boards.edges.map(({ node }) => ({ + return data[this.parentType][boardType].edges.map(({ node }) => ({ id: getIdFromGraphQLId(node.id), name: node.name, })); @@ -185,6 +189,9 @@ export default { boardQuery() { return this.isGroupBoard ? groupBoardsQuery : projectBoardsQuery; }, + recentBoardsQuery() { + return this.isGroupBoard ? groupRecentBoardsQuery : projectRecentBoardsQuery; + }, loadBoards(toggleDropdown = true) { if (toggleDropdown && this.boards.length > 0) { return; @@ -196,39 +203,20 @@ export default { }, query: this.boardQuery, loadingKey: 'loadingBoards', - update: this.boardUpdate, + update: (data) => this.boardUpdate(data, 'boards'), }); this.loadRecentBoards(); }, loadRecentBoards() { - this.loadingRecentBoards = true; - // Follow up to fetch recent boards using GraphQL - // https://gitlab.com/gitlab-org/gitlab/-/issues/300985 - axios - .get(this.recentBoardsEndpoint) - .then((res) => { - this.recentBoards = res.data; - }) - .catch((err) => { - /** - * If user is unauthorized we'd still want to resolve the - * request to display all boards. - */ - if (err?.response?.status === httpStatusCodes.UNAUTHORIZED) { - this.recentBoards = []; // recent boards are empty - return; - } - throw err; - }) - .then(() => this.$nextTick()) // Wait for boards list in DOM - .then(() => { - this.setScrollFade(); - }) - .catch(() => {}) - .finally(() => { - this.loadingRecentBoards = false; - }); + this.$apollo.addSmartQuery('recentBoards', { + variables() { + return { fullPath: this.fullPath }; + }, + query: this.recentBoardsQuery, + loadingKey: 'loadingRecentBoards', + update: (data) => this.boardUpdate(data, 'recentIssueBoards'), + }); }, isScrolledUp() { const { content } = this.$refs; diff --git a/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql new file mode 100644 index 0000000000000..827c08486b19b --- /dev/null +++ b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql @@ -0,0 +1,14 @@ +#import "ee_else_ce/boards/graphql/board.fragment.graphql" + +query group_recent_boards($fullPath: ID!) { + group(fullPath: $fullPath) { + id + recentIssueBoards { + edges { + node { + ...BoardFragment + } + } + } + } +} diff --git a/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql new file mode 100644 index 0000000000000..4d38e9b0498ee --- /dev/null +++ b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql @@ -0,0 +1,14 @@ +#import "ee_else_ce/boards/graphql/board.fragment.graphql" + +query project_recent_boards($fullPath: ID!) { + project(fullPath: $fullPath) { + id + recentIssueBoards { + edges { + node { + ...BoardFragment + } + } + } + } +} diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index ded3bfded868e..9f44380781e17 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -144,7 +144,6 @@ export default () => { mountMultipleBoardsSwitcher({ fullPath: $boardApp.dataset.fullPath, rootPath: $boardApp.dataset.boardsEndpoint, - recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint, allowScopedLabels: $boardApp.dataset.scopedLabels, labelsManagePath: $boardApp.dataset.labelsManagePath, }); diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index 26dd8b99f980a..3838b4f2a83d2 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -24,7 +24,6 @@ export default (params = {}) => { provide: { fullPath: params.fullPath, rootPath: params.rootPath, - recentBoardsEndpoint: params.recentBoardsEndpoint, allowScopedLabels: params.allowScopedLabels, labelsManagePath: params.labelsManagePath, allowLabelCreate: parseBoolean(dataset.canAdminBoard), diff --git a/ee/app/assets/javascripts/boards/components/boards_selector.vue b/ee/app/assets/javascripts/boards/components/boards_selector.vue index c37b087659e50..c8e0ebe5f324f 100644 --- a/ee/app/assets/javascripts/boards/components/boards_selector.vue +++ b/ee/app/assets/javascripts/boards/components/boards_selector.vue @@ -52,7 +52,8 @@ export default { }, query: this.isEpicBoard ? this.epicBoardQuery : this.boardQuery, loadingKey: 'loadingBoards', - update: this.isEpicBoard ? this.epicBoardUpdate : this.boardUpdate, + update: (data) => + this.isEpicBoard ? this.epicBoardUpdate(data) : this.boardUpdate(data, 'boards'), }); if (!this.isEpicBoard) { diff --git a/ee/spec/frontend/boards/components/boards_selector_spec.js b/ee/spec/frontend/boards/components/boards_selector_spec.js index 55c301a7a53a9..a89a96a045cd3 100644 --- a/ee/spec/frontend/boards/components/boards_selector_spec.js +++ b/ee/spec/frontend/boards/components/boards_selector_spec.js @@ -1,46 +1,35 @@ -import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } from '@gitlab/ui'; +import { GlDropdown } 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 BoardsSelector from 'ee/boards/components/boards_selector.vue'; import { BoardType } from '~/boards/constants'; import epicBoardQuery from 'ee/boards/graphql/epic_board.query.graphql'; +import epicBoardsQuery from 'ee/boards/graphql/epic_boards.query.graphql'; 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 defaultStore from '~/boards/stores'; import { TEST_HOST } from 'spec/test_constants'; -import axios from '~/lib/utils/axios_utils'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { mockGroupBoardResponse, mockProjectBoardResponse } from 'jest/boards/mock_data'; -import { mockEpicBoardResponse } from '../mock_data'; +import { + mockGroupBoardResponse, + mockProjectBoardResponse, + mockGroupAllBoardsResponse, + mockProjectAllBoardsResponse, +} from 'jest/boards/mock_data'; +import { mockEpicBoardResponse, mockEpicBoardsResponse } 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, @@ -63,20 +52,26 @@ describe('BoardsSelector', () => { }); }; - const getDropdownItems = () => wrapper.findAll('.js-dropdown-item'); - const getDropdownHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader); - const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findDropdown = () => wrapper.findComponent(GlDropdown); + const findDropdown = () => wrapper.find(GlDropdown); const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse); const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse); const epicBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockEpicBoardResponse); + const projectBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockProjectAllBoardsResponse); + const groupBoardsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupAllBoardsResponse); + const epicBoardsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockEpicBoardsResponse); + const createComponent = () => { fakeApollo = createMockApollo([ [projectBoardQuery, projectBoardQueryHandlerSuccess], [groupBoardQuery, groupBoardQueryHandlerSuccess], [epicBoardQuery, epicBoardQueryHandlerSuccess], + [projectBoardsQuery, projectBoardsQueryHandlerSuccess], + [groupBoardsQuery, groupBoardsQueryHandlerSuccess], + [epicBoardsQuery, epicBoardsQueryHandlerSuccess], ]); wrapper = mount(BoardsSelector, { @@ -94,92 +89,44 @@ 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; fakeApollo = null; store = null; - mock.restore(); }); - describe('fetching all board', () => { + describe('fetching all boards', () => { beforeEach(() => { - mock = new MockAdapter(axios); - - allBoardsResponse = Promise.resolve({ - data: { - group: { - boards: { - edges: boards.map((board) => ({ node: board })), - }, - }, + it.each` + boardType | isEpicBoard | queryHandler | notCalledHandler + ${BoardType.group} | ${false} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess} + ${BoardType.project} | ${false} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess} + ${BoardType.group} | ${true} | ${epicBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess} + `( + 'fetches $boardType boards when isEpicBoard is $isEpicBoard', + async ({ boardType, isEpicBoard, queryHandler, notCalledHandler }) => { + createStore({ + isProjectBoard: boardType === BoardType.project, + isGroupBoard: boardType === BoardType.group, + isEpicBoard, + }); + 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(); }, - }); - recentBoardsResponse = Promise.resolve({ - data: recentBoards, - }); - - createStore(); - 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', () => { - expect(getDropdownHeaders()).toHaveLength(0); - expect(getDropdownItems()).toHaveLength(0); - expect(getLoadingIcon().exists()).toBe(true); - }); - }); - - describe('loaded', () => { - 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'); - - // 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, - }); - }); - - it('hides loading spinner', async () => { - await nextTick(); - expect(getLoadingIcon().exists()).toBe(false); - }); + ); }); }); diff --git a/ee/spec/frontend/boards/mock_data.js b/ee/spec/frontend/boards/mock_data.js index b5d9a197bc454..83034e638159b 100644 --- a/ee/spec/frontend/boards/mock_data.js +++ b/ee/spec/frontend/boards/mock_data.js @@ -21,6 +21,27 @@ export const mockEpicBoardResponse = { }, }; +export const mockEpicBoardsResponse = { + data: { + group: { + id: 'gid://gitlab/Group/114', + epicBoards: { + nodes: [ + { + id: 'gid://gitlab/Boards::EpicBoard/1', + name: 'Development', + }, + { + id: 'gid://gitlab/Boards::EpicBoard/2', + name: 'Marketing', + }, + ], + }, + __typename: 'Group', + }, + }, +}; + export const mockLabel = { id: 'gid://gitlab/GroupLabel/121', title: 'To Do', diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js index 9cf7c5774bf95..1ee318cbe279e 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, + mockSmallGroupAllBoardsResponse, + mockEmptyGroupRecentBoardsResponse, + 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({ @@ -70,10 +67,36 @@ describe('BoardsSelector', () => { 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(mockSmallGroupAllBoardsResponse); + const emptyRecentBoardsQueryHandlerSuccess = jest + .fn() + .mockResolvedValue(mockEmptyGroupRecentBoardsResponse); + + const createComponent = ({ + groupBoardsQueryHandler = groupBoardsQueryHandlerSuccess, + groupRecentBoardsQueryHandler = groupRecentBoardsQueryHandlerSuccess, + } = {}) => { fakeApollo = createMockApollo([ [projectBoardQuery, projectBoardQueryHandlerSuccess], [groupBoardQuery, groupBoardQueryHandlerSuccess], + [projectBoardsQuery, projectBoardsQueryHandlerSuccess], + [groupBoardsQuery, groupBoardsQueryHandler], + [projectRecentBoardsQuery, projectRecentBoardsQueryHandlerSuccess], + [groupRecentBoardsQuery, groupRecentBoardsQueryHandler], ]); wrapper = mount(BoardsSelector, { @@ -91,67 +114,33 @@ 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(); }); - 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({ isGroupBoard: 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 +152,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(groupBoardsQueryHandlerSuccess).toHaveBeenCalled(); + }); + it('hides loading spinner', async () => { await nextTick(); expect(getLoadingIcon().exists()).toBe(false); @@ -180,22 +166,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,33 +195,20 @@ 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(groupRecentBoardsQueryHandlerSuccess).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({ groupBoardsQueryHandler: 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({ groupRecentBoardsQueryHandler: emptyRecentBoardsQueryHandlerSuccess }); await nextTick(); expect(getDropdownHeaders()).toHaveLength(0); @@ -256,15 +224,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/mock_data.js b/spec/frontend/boards/mock_data.js index 254965827072e..ec6c567ded488 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -29,6 +29,84 @@ export const listObj = { }, }; +function boardGenerator(n) { + return new Array(n).fill().map((board, index) => { + const id = `${index}`; + const name = `board${id}`; + + return { + node: { + id, + name, + weight: 0, + }, + }; + }); +} + +export const boards = boardGenerator(20); +export const recentIssueBoards = boardGenerator(5); + +export const mockSmallGroupAllBoardsResponse = { + data: { + group: { + id: 'gid://gitlab/Group/114', + boards: { edges: boardGenerator(3) }, + __typename: 'Group', + }, + }, +}; + +export const mockEmptyGroupRecentBoardsResponse = { + data: { + group: { + id: 'gid://gitlab/Group/114', + recentIssueBoards: { edges: [] }, + __typename: 'Group', + }, + }, +}; + +export const mockGroupAllBoardsResponse = { + data: { + group: { + id: 'gid://gitlab/Group/114', + boards: { edges: boards }, + __typename: 'Group', + }, + }, +}; + +export const mockProjectAllBoardsResponse = { + data: { + project: { + id: 'gid://gitlab/Project/1', + boards: { edges: boards }, + __typename: 'Project', + }, + }, +}; + +export const mockGroupRecentBoardsResponse = { + data: { + group: { + id: 'gid://gitlab/Group/114', + recentIssueBoards: { edges: recentIssueBoards }, + __typename: 'Group', + }, + }, +}; + +export const mockProjectRecentBoardsResponse = { + data: { + project: { + id: 'gid://gitlab/Project/1', + recentIssueBoards: { edges: recentIssueBoards }, + __typename: 'Project', + }, + }, +}; + export const mockGroupBoardResponse = { data: { workspace: { -- GitLab