From d83a3edd4416e93f2815815c1be4ee0a2755a3c5 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 11 Dec 2023 15:15:12 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- spec/frontend/boards/board_list_helper.js | 61 +- spec/frontend/boards/board_list_spec.js | 35 +- .../boards/components/board_add_new_column_spec.js | 193 ++---- spec/frontend/boards/components/board_card_spec.js | 101 +--- .../components/board_content_sidebar_spec.js | 79 +-- .../components/board_settings_sidebar_spec.js | 113 +--- .../boards/components/boards_selector_spec.js | 29 +- spec/frontend/boards/mock_data.js | 70 ++- spec/frontend/boards/stores/mutations_spec.js | 672 --------------------- .../setup_instructions_spec.js | 9 + .../ml/model_registry/apps/index_ml_models_spec.js | 11 +- .../notes/components/discussion_filter_spec.js | 49 +- 12 files changed, 240 insertions(+), 1182 deletions(-) delete mode 100644 spec/frontend/boards/stores/mutations_spec.js (limited to 'spec/frontend') diff --git a/spec/frontend/boards/board_list_helper.js b/spec/frontend/boards/board_list_helper.js index 5bafd9a8d0e..e3afd2dec2f 100644 --- a/spec/frontend/boards/board_list_helper.js +++ b/spec/frontend/boards/board_list_helper.js @@ -1,34 +1,22 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import BoardCard from '~/boards/components/board_card.vue'; import BoardList from '~/boards/components/board_list.vue'; import BoardNewIssue from '~/boards/components/board_new_issue.vue'; import BoardNewItem from '~/boards/components/board_new_item.vue'; -import defaultState from '~/boards/stores/state'; + import createMockApollo from 'helpers/mock_apollo_helper'; import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql'; -import { - mockList, - mockIssuesByListId, - issues, - mockGroupProjects, - boardListQueryResponse, -} from './mock_data'; +import { mockList, boardListQueryResponse } from './mock_data'; export default function createComponent({ - listIssueProps = {}, componentProps = {}, listProps = {}, apolloQueryHandlers = [], - actions = {}, - getters = {}, provide = {}, data = {}, - state = defaultState, stubs = { BoardNewIssue, BoardNewItem, @@ -37,60 +25,25 @@ export default function createComponent({ issuesCount, } = {}) { Vue.use(VueApollo); - Vue.use(Vuex); const fakeApollo = createMockApollo([ [listQuery, jest.fn().mockResolvedValue(boardListQueryResponse({ issuesCount }))], ...apolloQueryHandlers, ]); - const store = new Vuex.Store({ - state: { - selectedProject: mockGroupProjects[0], - boardItemsByListId: mockIssuesByListId, - boardItems: issues, - pageInfoByListId: { - 'gid://gitlab/List/1': { hasNextPage: true }, - 'gid://gitlab/List/2': {}, - }, - listsFlags: { - 'gid://gitlab/List/1': {}, - 'gid://gitlab/List/2': {}, - }, - selectedBoardItems: [], - ...state, - }, - getters: { - isEpicBoard: () => false, - ...getters, - }, - actions, - }); - const list = { ...mockList, ...listProps, }; - const issue = { - title: 'Testing', - id: 1, - iid: 1, - confidential: false, - referencePath: 'gitlab-org/test-subgroup/gitlab-test#1', - labels: [], - assignees: [], - ...listIssueProps, - }; + if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesCount')) { list.issuesCount = 1; } const component = shallowMount(BoardList, { apolloProvider: fakeApollo, - store, propsData: { list, - boardItems: [issue], canAdminList: true, boardId: 'gid://gitlab/Board/1', filterParams: {}, @@ -106,12 +59,12 @@ export default function createComponent({ canAdminList: true, isIssueBoard: true, isEpicBoard: false, - isGroupBoard: false, - isProjectBoard: true, + isGroupBoard: true, + isProjectBoard: false, disabled: false, boardType: 'group', issuableType: 'issue', - isApolloBoard: false, + isApolloBoard: true, ...provide, }, stubs, @@ -122,7 +75,5 @@ export default function createComponent({ }, }); - jest.spyOn(store, 'dispatch').mockImplementation(() => {}); - return component; } diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js index 30bb4fba4e3..8d59cb2692e 100644 --- a/spec/frontend/boards/board_list_spec.js +++ b/spec/frontend/boards/board_list_spec.js @@ -8,8 +8,9 @@ import createComponent from 'jest/boards/board_list_helper'; import BoardCard from '~/boards/components/board_card.vue'; import eventHub from '~/boards/eventhub'; import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue'; +import listIssuesQuery from '~/boards/graphql/lists_issues.query.graphql'; -import { mockIssues, mockList, mockIssuesMore } from './mock_data'; +import { mockIssues, mockList, mockIssuesMore, mockGroupIssuesResponse } from './mock_data'; describe('Board list component', () => { let wrapper; @@ -41,8 +42,13 @@ describe('Board list component', () => { useFakeRequestAnimationFrame(); describe('When Expanded', () => { - beforeEach(() => { - wrapper = createComponent({ issuesCount: 1 }); + beforeEach(async () => { + wrapper = createComponent({ + apolloQueryHandlers: [ + [listIssuesQuery, jest.fn().mockResolvedValue(mockGroupIssuesResponse())], + ], + }); + await waitForPromises(); }); it('renders component', () => { @@ -62,7 +68,7 @@ describe('Board list component', () => { }); it('sets data attribute with issue id', () => { - expect(wrapper.find('.board-card').attributes('data-item-id')).toBe('1'); + expect(wrapper.find('.board-card').attributes('data-item-id')).toBe('gid://gitlab/Issue/436'); }); it('shows new issue form after eventhub event', async () => { @@ -107,19 +113,18 @@ describe('Board list component', () => { describe('load more issues', () => { describe('when loading is not in progress', () => { - beforeEach(() => { + beforeEach(async () => { wrapper = createComponent({ - listProps: { - id: 'gid://gitlab/List/1', - }, - componentProps: { - boardItems: mockIssuesMore, - }, - actions: { - fetchItemsForList: jest.fn(), - }, - state: { listsFlags: { 'gid://gitlab/List/1': { isLoadingMore: false } } }, + apolloQueryHandlers: [ + [ + listIssuesQuery, + jest + .fn() + .mockResolvedValue(mockGroupIssuesResponse('gid://gitlab/List/1', mockIssuesMore)), + ], + ], }); + await waitForPromises(); }); it('has intersection observer when the number of board list items are more than 5', () => { diff --git a/spec/frontend/boards/components/board_add_new_column_spec.js b/spec/frontend/boards/components/board_add_new_column_spec.js index 1a847d35900..768a93f6970 100644 --- a/spec/frontend/boards/components/board_add_new_column_spec.js +++ b/spec/frontend/boards/components/board_add_new_column_spec.js @@ -1,14 +1,11 @@ import { GlCollapsibleListbox } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import BoardAddNewColumn from '~/boards/components/board_add_new_column.vue'; import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form.vue'; -import defaultState from '~/boards/stores/state'; import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql'; import boardLabelsQuery from '~/boards/graphql/board_labels.query.graphql'; import * as cacheUpdates from '~/boards/graphql/cache_updates'; @@ -19,7 +16,6 @@ import { boardListsQueryResponse, } from '../mock_data'; -Vue.use(Vuex); Vue.use(VueApollo); describe('BoardAddNewColumn', () => { @@ -39,22 +35,8 @@ describe('BoardAddNewColumn', () => { findDropdown().vm.$emit('select', id); }; - const createStore = ({ actions = {}, getters = {}, state = {} } = {}) => { - return new Vuex.Store({ - state: { - ...defaultState, - ...state, - }, - actions, - getters, - }); - }; - const mountComponent = ({ selectedId, - labels = [], - getListByLabelId = jest.fn(), - actions = {}, provide = {}, lists = {}, labelsHandler = labelsQueryHandler, @@ -83,26 +65,12 @@ describe('BoardAddNewColumn', () => { selectedId, }; }, - store: createStore({ - actions: { - fetchLabels: jest.fn(), - ...actions, - }, - getters: { - getListByLabelId: () => getListByLabelId, - }, - state: { - labels, - labelsLoading: false, - }, - }), provide: { scopedLabelsAvailable: true, isEpicBoard: false, issuableType: 'issue', fullPath: 'gitlab-org/gitlab', boardType: 'project', - isApolloBoard: false, ...provide, }, stubs: { @@ -126,149 +94,94 @@ describe('BoardAddNewColumn', () => { cacheUpdates.setError = jest.fn(); }); - describe('Add list button', () => { - it('calls addList', async () => { - const getListByLabelId = jest.fn().mockReturnValue(null); - const highlightList = jest.fn(); - const createList = jest.fn(); + describe('when list is new', () => { + beforeEach(() => { + mountComponent({ selectedId: mockLabelList.label.id }); + }); - mountComponent({ - labels: [mockLabelList.label], - selectedId: mockLabelList.label.id, - getListByLabelId, - actions: { - createList, - highlightList, - }, - }); + it('fetches labels and adds list', async () => { + findDropdown().vm.$emit('show'); + + await nextTick(); + expect(labelsQueryHandler).toHaveBeenCalled(); + + selectLabel(mockLabelList.label.id); findAddNewColumnForm().vm.$emit('add-list'); await nextTick(); - expect(highlightList).not.toHaveBeenCalled(); - expect(createList).toHaveBeenCalledWith(expect.anything(), { + expect(wrapper.emitted('highlight-list')).toBeUndefined(); + expect(createBoardListQueryHandler).toHaveBeenCalledWith({ labelId: mockLabelList.label.id, + boardId: 'gid://gitlab/Board/1', }); }); + }); - it('highlights existing list if trying to re-add', async () => { - const getListByLabelId = jest.fn().mockReturnValue(mockLabelList); - const highlightList = jest.fn(); - const createList = jest.fn(); - + describe('when list already exists in board', () => { + beforeEach(() => { mountComponent({ - labels: [mockLabelList.label], - selectedId: mockLabelList.label.id, - getListByLabelId, - actions: { - createList, - highlightList, + lists: { + [mockLabelList.id]: mockLabelList, }, + selectedId: mockLabelList.label.id, }); - - findAddNewColumnForm().vm.$emit('add-list'); - - await nextTick(); - - expect(highlightList).toHaveBeenCalledWith(expect.anything(), mockLabelList.id); - expect(createList).not.toHaveBeenCalled(); }); - }); - describe('Apollo boards', () => { - describe('when list is new', () => { - beforeEach(() => { - mountComponent({ selectedId: mockLabelList.label.id, provide: { isApolloBoard: true } }); - }); - - it('fetches labels and adds list', async () => { - findDropdown().vm.$emit('show'); + it('highlights existing list if trying to re-add', async () => { + findDropdown().vm.$emit('show'); - await nextTick(); - expect(labelsQueryHandler).toHaveBeenCalled(); + await nextTick(); + expect(labelsQueryHandler).toHaveBeenCalled(); - selectLabel(mockLabelList.label.id); + selectLabel(mockLabelList.label.id); - findAddNewColumnForm().vm.$emit('add-list'); + findAddNewColumnForm().vm.$emit('add-list'); - await nextTick(); + await waitForPromises(); - expect(wrapper.emitted('highlight-list')).toBeUndefined(); - expect(createBoardListQueryHandler).toHaveBeenCalledWith({ - labelId: mockLabelList.label.id, - boardId: 'gid://gitlab/Board/1', - }); - }); + expect(wrapper.emitted('highlight-list')).toEqual([[mockLabelList.id]]); + expect(createBoardListQueryHandler).not.toHaveBeenCalledWith(); }); + }); - describe('when list already exists in board', () => { - beforeEach(() => { - mountComponent({ - lists: { - [mockLabelList.id]: mockLabelList, - }, - selectedId: mockLabelList.label.id, - provide: { isApolloBoard: true }, - }); - }); - - it('highlights existing list if trying to re-add', async () => { - findDropdown().vm.$emit('show'); - - await nextTick(); - expect(labelsQueryHandler).toHaveBeenCalled(); - - selectLabel(mockLabelList.label.id); - - findAddNewColumnForm().vm.$emit('add-list'); - - await waitForPromises(); - - expect(wrapper.emitted('highlight-list')).toEqual([[mockLabelList.id]]); - expect(createBoardListQueryHandler).not.toHaveBeenCalledWith(); + describe('when fetch labels query fails', () => { + beforeEach(() => { + mountComponent({ + labelsHandler: labelsQueryHandlerFailure, }); }); - describe('when fetch labels query fails', () => { - beforeEach(() => { - mountComponent({ - provide: { isApolloBoard: true }, - labelsHandler: labelsQueryHandlerFailure, - }); - }); + it('sets error', async () => { + findDropdown().vm.$emit('show'); - it('sets error', async () => { - findDropdown().vm.$emit('show'); - - await waitForPromises(); - expect(cacheUpdates.setError).toHaveBeenCalled(); - }); + await waitForPromises(); + expect(cacheUpdates.setError).toHaveBeenCalled(); }); + }); - describe('when create list mutation fails', () => { - beforeEach(() => { - mountComponent({ - selectedId: mockLabelList.label.id, - provide: { isApolloBoard: true }, - createHandler: createBoardListQueryHandlerFailure, - }); + describe('when create list mutation fails', () => { + beforeEach(() => { + mountComponent({ + selectedId: mockLabelList.label.id, + createHandler: createBoardListQueryHandlerFailure, }); + }); - it('sets error', async () => { - findDropdown().vm.$emit('show'); + it('sets error', async () => { + findDropdown().vm.$emit('show'); - await nextTick(); - expect(labelsQueryHandler).toHaveBeenCalled(); + await nextTick(); + expect(labelsQueryHandler).toHaveBeenCalled(); - selectLabel(mockLabelList.label.id); + selectLabel(mockLabelList.label.id); - findAddNewColumnForm().vm.$emit('add-list'); + findAddNewColumnForm().vm.$emit('add-list'); - await waitForPromises(); + await waitForPromises(); - expect(cacheUpdates.setError).toHaveBeenCalled(); - }); + expect(cacheUpdates.setError).toHaveBeenCalled(); }); }); }); diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js index 11f9a4f6ff2..dae0db27104 100644 --- a/spec/frontend/boards/components/board_card_spec.js +++ b/spec/frontend/boards/components/board_card_spec.js @@ -1,7 +1,5 @@ import { GlLabel } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import VueApollo from 'vue-apollo'; import waitForPromises from 'helpers/wait_for_promises'; @@ -9,17 +7,14 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import BoardCard from '~/boards/components/board_card.vue'; import BoardCardInner from '~/boards/components/board_card_inner.vue'; -import { inactiveId } from '~/boards/constants'; import selectedBoardItemsQuery from '~/boards/graphql/client/selected_board_items.query.graphql'; +import activeBoardItemQuery from '~/boards/graphql/client/active_board_item.query.graphql'; import isShowingLabelsQuery from '~/graphql_shared/client/is_showing_labels.query.graphql'; import { mockLabelList, mockIssue, DEFAULT_COLOR } from '../mock_data'; describe('Board card', () => { let wrapper; - let store; - let mockActions; - Vue.use(Vuex); Vue.use(VueApollo); const mockSetActiveBoardItemResolver = jest.fn(); @@ -31,23 +26,6 @@ describe('Board card', () => { }, }); - const createStore = ({ initialState = {} } = {}) => { - mockActions = { - toggleBoardItem: jest.fn(), - toggleBoardItemMultiSelection: jest.fn(), - performSearch: jest.fn(), - }; - - store = new Vuex.Store({ - state: { - activeId: inactiveId, - selectedBoardItems: [], - ...initialState, - }, - actions: mockActions, - }); - }; - // this particular mount component needs to be used after the root beforeEach because it depends on list being initialized const mountComponent = ({ propsData = {}, @@ -55,6 +33,7 @@ describe('Board card', () => { stubs = { BoardCardInner }, item = mockIssue, selectedBoardItems = [], + activeBoardItem = {}, } = {}) => { mockApollo.clients.defaultClient.cache.writeQuery({ query: isShowingLabelsQuery, @@ -68,6 +47,12 @@ describe('Board card', () => { selectedBoardItems, }, }); + mockApollo.clients.defaultClient.cache.writeQuery({ + query: activeBoardItemQuery, + data: { + activeBoardItem, + }, + }); wrapper = shallowMountExtended(BoardCard, { apolloProvider: mockApollo, @@ -75,7 +60,6 @@ describe('Board card', () => { ...stubs, BoardCardInner, }, - store, propsData: { list: mockLabelList, item, @@ -92,7 +76,7 @@ describe('Board card', () => { isGroupBoard: true, disabled: false, allowSubEpics: false, - isApolloBoard: false, + isApolloBoard: true, ...provide, }, }); @@ -112,47 +96,32 @@ describe('Board card', () => { window.gon = { features: {} }; }); - afterEach(() => { - store = null; - }); - describe('when GlLabel is clicked in BoardCardInner', () => { - it('doesnt call toggleBoardItem', () => { - createStore(); + it("doesn't call setSelectedBoardItemsMutation", () => { mountComponent(); wrapper.findComponent(GlLabel).trigger('mouseup'); - expect(mockActions.toggleBoardItem).toHaveBeenCalledTimes(0); + expect(mockSetSelectedBoardItemsResolver).toHaveBeenCalledTimes(0); }); }); it('should not highlight the card by default', () => { - createStore(); mountComponent(); expect(wrapper.classes()).not.toContain('is-active'); expect(wrapper.classes()).not.toContain('multi-select'); }); - it('should highlight the card with a correct style when selected', () => { - createStore({ - initialState: { - activeId: mockIssue.id, - }, - }); - mountComponent(); + it('should highlight the card with a correct style when selected', async () => { + mountComponent({ activeBoardItem: mockIssue }); + await waitForPromises(); expect(wrapper.classes()).toContain('is-active'); expect(wrapper.classes()).not.toContain('multi-select'); }); it('should highlight the card with a correct style when multi-selected', () => { - createStore({ - initialState: { - activeId: inactiveId, - }, - }); mountComponent({ selectedBoardItems: [mockIssue.id] }); expect(wrapper.classes()).toContain('multi-select'); @@ -161,18 +130,22 @@ describe('Board card', () => { describe('when mouseup event is called on the card', () => { beforeEach(() => { - createStore(); mountComponent(); }); describe('when not using multi-select', () => { - it('should call vuex action "toggleBoardItem" with correct parameters', async () => { + it('set active board item on client when clicking on card', async () => { await selectCard(); + await waitForPromises(); - expect(mockActions.toggleBoardItem).toHaveBeenCalledTimes(1); - expect(mockActions.toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), { - boardItem: mockIssue, - }); + expect(mockSetActiveBoardItemResolver).toHaveBeenCalledWith( + {}, + { + boardItem: mockIssue, + }, + expect.anything(), + expect.anything(), + ); }); }); @@ -199,7 +172,6 @@ describe('Board card', () => { describe('when card is loading', () => { it('card is disabled and user cannot drag', () => { - createStore(); mountComponent({ item: { ...mockIssue, isLoading: true } }); expect(wrapper.classes()).toContain('is-disabled'); @@ -209,7 +181,6 @@ describe('Board card', () => { describe('when card is not loading', () => { it('user can drag', () => { - createStore(); mountComponent(); expect(wrapper.classes()).not.toContain('is-disabled'); @@ -220,7 +191,6 @@ describe('Board card', () => { describe('when Epic colors are enabled', () => { it('applies the correct color', () => { window.gon.features = { epicColorHighlight: true }; - createStore(); mountComponent({ item: { ...mockIssue, @@ -238,7 +208,6 @@ describe('Board card', () => { describe('when Epic colors are not enabled', () => { it('applies the correct color', () => { window.gon.features = { epicColorHighlight: false }; - createStore(); mountComponent({ item: { ...mockIssue, @@ -252,26 +221,4 @@ describe('Board card', () => { expect(wrapper.attributes('style')).toBeUndefined(); }); }); - - describe('Apollo boards', () => { - beforeEach(async () => { - createStore(); - mountComponent({ provide: { isApolloBoard: true } }); - await nextTick(); - }); - - it('set active board item on client when clicking on card', async () => { - await selectCard(); - await waitForPromises(); - - expect(mockSetActiveBoardItemResolver).toHaveBeenCalledWith( - {}, - { - boardItem: mockIssue, - }, - expect.anything(), - expect.anything(), - ); - }); - }); }); diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js index 01eea12bf0a..5fffd4d0c23 100644 --- a/spec/frontend/boards/components/board_content_sidebar_spec.js +++ b/spec/frontend/boards/components/board_content_sidebar_spec.js @@ -2,8 +2,6 @@ import { GlDrawer } from '@gitlab/ui'; import { MountingPortal } from 'portal-vue'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import { stubComponent } from 'helpers/stub_component'; @@ -13,20 +11,17 @@ import waitForPromises from 'helpers/wait_for_promises'; import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql'; import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue'; import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue'; -import { ISSUABLE } from '~/boards/constants'; import { TYPE_ISSUE } from '~/issues/constants'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarSeverityWidget from '~/sidebar/components/severity/sidebar_severity_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import SidebarLabelsWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue'; -import { mockActiveIssue, mockIssue, rawIssue } from '../mock_data'; +import { rawIssue } from '../mock_data'; -Vue.use(Vuex); Vue.use(VueApollo); describe('BoardContentSidebar', () => { let wrapper; - let store; const mockSetActiveBoardItemResolver = jest.fn(); const mockApollo = createMockApollo([], { @@ -35,28 +30,11 @@ describe('BoardContentSidebar', () => { }, }); - const createStore = ({ mockGetters = {}, mockActions = {} } = {}) => { - store = new Vuex.Store({ - state: { - sidebarType: ISSUABLE, - issues: { [mockIssue.id]: { ...mockIssue, epic: null } }, - activeId: mockIssue.id, - }, - getters: { - activeBoardItem: () => { - return { ...mockActiveIssue, epic: null }; - }, - ...mockGetters, - }, - actions: mockActions, - }); - }; - - const createComponent = ({ isApolloBoard = false } = {}) => { + const createComponent = ({ issuable = rawIssue } = {}) => { mockApollo.clients.defaultClient.cache.writeQuery({ query: activeBoardItemQuery, data: { - activeBoardItem: rawIssue, + activeBoardItem: issuable, }, }); @@ -68,9 +46,7 @@ describe('BoardContentSidebar', () => { groupId: 1, issuableType: TYPE_ISSUE, isGroupBoard: false, - isApolloBoard, }, - store, stubs: { GlDrawer: stubComponent(GlDrawer, { template: '
', @@ -80,7 +56,6 @@ describe('BoardContentSidebar', () => { }; beforeEach(() => { - createStore(); createComponent(); }); @@ -97,8 +72,7 @@ describe('BoardContentSidebar', () => { }); it('does not render GlDrawer when no active item is set', async () => { - createStore({ mockGetters: { activeBoardItem: () => ({ id: '', iid: '' }) } }); - createComponent(); + createComponent({ issuable: {} }); await nextTick(); @@ -155,45 +129,10 @@ describe('BoardContentSidebar', () => { }); describe('when we emit close', () => { - let toggleBoardItem; - - beforeEach(() => { - toggleBoardItem = jest.fn(); - createStore({ mockActions: { toggleBoardItem } }); - createComponent(); - }); - - it('calls toggleBoardItem with correct parameters', () => { - wrapper.findComponent(GlDrawer).vm.$emit('close'); - - expect(toggleBoardItem).toHaveBeenCalledTimes(1); - expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), { - boardItem: { ...mockActiveIssue, epic: null }, - sidebarType: ISSUABLE, - }); - }); - }); - - describe('incident sidebar', () => { beforeEach(() => { - createStore({ - mockGetters: { activeBoardItem: () => ({ ...mockIssue, epic: null, type: 'INCIDENT' }) }, - }); createComponent(); }); - it('renders SidebarSeverityWidget', () => { - expect(wrapper.findComponent(SidebarSeverityWidget).exists()).toBe(true); - }); - }); - - describe('Apollo boards', () => { - beforeEach(async () => { - createStore(); - createComponent({ isApolloBoard: true }); - await nextTick(); - }); - it('calls setActiveBoardItemMutation on close', async () => { wrapper.findComponent(GlDrawer).vm.$emit('close'); @@ -209,4 +148,14 @@ describe('BoardContentSidebar', () => { ); }); }); + + describe('incident sidebar', () => { + beforeEach(() => { + createComponent({ issuable: { ...rawIssue, epic: null, type: 'INCIDENT' } }); + }); + + it('renders SidebarSeverityWidget', () => { + expect(wrapper.findComponent(SidebarSeverityWidget).exists()).toBe(true); + }); + }); }); diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js index f6ed483dfc5..71c886351b6 100644 --- a/spec/frontend/boards/components/board_settings_sidebar_spec.js +++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js @@ -3,32 +3,23 @@ import { shallowMount } from '@vue/test-utils'; import { MountingPortal } from 'portal-vue'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import createMockApollo from 'helpers/mock_apollo_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import waitForPromises from 'helpers/wait_for_promises'; import { stubComponent } from 'helpers/stub_component'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue'; -import { inactiveId, LIST } from '~/boards/constants'; import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql'; import * as cacheUpdates from '~/boards/graphql/cache_updates'; -import actions from '~/boards/stores/actions'; -import getters from '~/boards/stores/getters'; -import mutations from '~/boards/stores/mutations'; -import sidebarEventHub from '~/sidebar/event_hub'; import { mockLabelList, destroyBoardListMutationResponse } from '../mock_data'; Vue.use(VueApollo); -Vue.use(Vuex); describe('BoardSettingsSidebar', () => { let wrapper; let mockApollo; const labelTitle = mockLabelList.label.title; const labelColor = mockLabelList.label.color; - const listId = mockLabelList.id; const modalID = 'board-settings-sidebar-modal'; const destroyBoardListMutationHandlerSuccess = jest @@ -42,26 +33,12 @@ describe('BoardSettingsSidebar', () => { const createComponent = ({ canAdminList = false, list = {}, - sidebarType = LIST, - activeId = inactiveId, destroyBoardListMutationHandler = destroyBoardListMutationHandlerSuccess, - isApolloBoard = false, } = {}) => { - const boardLists = { - [listId]: list, - }; - const store = new Vuex.Store({ - state: { sidebarType, activeId, boardLists }, - getters, - mutations, - actions, - }); - mockApollo = createMockApollo([[destroyBoardListMutation, destroyBoardListMutationHandler]]); wrapper = extendedWrapper( shallowMount(BoardSettingsSidebar, { - store, apolloProvider: mockApollo, provide: { canAdminList, @@ -69,7 +46,6 @@ describe('BoardSettingsSidebar', () => { isIssueBoard: true, boardType: 'group', issuableType: 'issue', - isApolloBoard, }, propsData: { listId: list.id || '', @@ -100,90 +76,50 @@ describe('BoardSettingsSidebar', () => { cacheUpdates.setError = jest.fn(); }); - it('finds a MountingPortal component', () => { - createComponent(); - - expect(wrapper.findComponent(MountingPortal).props()).toMatchObject({ - mountTo: '#js-right-sidebar-portal', - append: true, - name: 'board-settings-sidebar', - }); - }); - - describe('when sidebarType is "list"', () => { - it('finds a GlDrawer component', () => { + describe('default', () => { + beforeEach(() => { createComponent(); + }); + it('renders a MountingPortal component', () => { + expect(wrapper.findComponent(MountingPortal).props()).toMatchObject({ + mountTo: '#js-right-sidebar-portal', + append: true, + name: 'board-settings-sidebar', + }); + }); + it('renders a GlDrawer component', () => { expect(findDrawer().exists()).toBe(true); }); describe('on close', () => { it('closes the sidebar', async () => { - createComponent(); - findDrawer().vm.$emit('close'); await nextTick(); expect(wrapper.findComponent(GlDrawer).props('open')).toBe(false); }); - - it('closes the sidebar when emitting the correct event', async () => { - createComponent(); - - sidebarEventHub.$emit('sidebar.closeAll'); - - await nextTick(); - - expect(wrapper.findComponent(GlDrawer).props('open')).toBe(false); - }); }); - describe('when activeId is zero', () => { + describe('when there is no active list', () => { it('renders GlDrawer with open false', () => { createComponent(); expect(findDrawer().props('open')).toBe(false); + expect(findLabel().exists()).toBe(false); }); }); - describe('when activeId is greater than zero', () => { - it('renders GlDrawer with open true', () => { - createComponent({ list: mockLabelList, activeId: listId }); + describe('when there is an active list', () => { + it('renders GlDrawer with list title and label', () => { + createComponent({ list: mockLabelList }); expect(findDrawer().props('open')).toBe(true); - }); - }); - - describe('when activeId is in state', () => { - it('renders label title', () => { - createComponent({ list: mockLabelList, activeId: listId }); - expect(findLabel().props('title')).toBe(labelTitle); - }); - - it('renders label background color', () => { - createComponent({ list: mockLabelList, activeId: listId }); - expect(findLabel().props('backgroundColor')).toBe(labelColor); }); }); - - describe('when activeId is not in state', () => { - it('does not render GlLabel', () => { - createComponent({ list: mockLabelList }); - - expect(findLabel().exists()).toBe(false); - }); - }); - }); - - describe('when sidebarType is not List', () => { - it('does not render GlDrawer', () => { - createComponent({ sidebarType: '' }); - - expect(findDrawer().props('open')).toBe(false); - }); }); it('does not render "Remove list" when user cannot admin the boards list', () => { @@ -193,20 +129,15 @@ describe('BoardSettingsSidebar', () => { }); describe('when user can admin the boards list', () => { - it('renders "Remove list" button', () => { - createComponent({ canAdminList: true, activeId: listId, list: mockLabelList }); + beforeEach(() => { + createComponent({ canAdminList: true, list: mockLabelList }); + }); + it('renders "Remove list" button', () => { expect(findRemoveButton().exists()).toBe(true); }); it('removes the list', () => { - createComponent({ - canAdminList: true, - activeId: listId, - list: mockLabelList, - isApolloBoard: true, - }); - findRemoveButton().vm.$emit('click'); wrapper.findComponent(GlModal).vm.$emit('primary'); @@ -215,23 +146,19 @@ describe('BoardSettingsSidebar', () => { }); 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); }); it('sets error when destroy list mutation fails', async () => { createComponent({ canAdminList: true, - activeId: listId, list: mockLabelList, destroyBoardListMutationHandler: destroyBoardListMutationHandlerFailure, - isApolloBoard: true, }); findRemoveButton().vm.$emit('click'); diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js index 0a628af9939..8766b1c25f2 100644 --- a/spec/frontend/boards/components/boards_selector_spec.js +++ b/spec/frontend/boards/components/boards_selector_spec.js @@ -1,8 +1,6 @@ import { GlCollapsibleListbox } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -// eslint-disable-next-line no-restricted-imports -import Vuex from 'vuex'; import waitForPromises from 'helpers/wait_for_promises'; import { TEST_HOST } from 'spec/test_constants'; import BoardsSelector from '~/boards/components/boards_selector.vue'; @@ -29,23 +27,10 @@ import { const throttleDuration = 1; Vue.use(VueApollo); -Vue.use(Vuex); describe('BoardsSelector', () => { let wrapper; let fakeApollo; - let store; - - const createStore = () => { - store = new Vuex.Store({ - actions: { - setBoardConfig: jest.fn(), - }, - state: { - board: mockBoard, - }, - }); - }; const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox); @@ -91,10 +76,10 @@ describe('BoardsSelector', () => { ]); wrapper = shallowMountExtended(BoardsSelector, { - store, apolloProvider: fakeApollo, propsData: { throttleDuration, + board: mockBoard, ...props, }, attachTo: document.body, @@ -109,7 +94,7 @@ describe('BoardsSelector', () => { boardType: isGroupBoard ? 'group' : 'project', isGroupBoard, isProjectBoard, - isApolloBoard: false, + // isApolloBoard: false, ...provide, }, }); @@ -125,7 +110,6 @@ describe('BoardsSelector', () => { describe('template', () => { beforeEach(() => { - createStore(); createComponent({ isProjectBoard: true }); }); @@ -137,9 +121,6 @@ describe('BoardsSelector', () => { it('shows loading spinner', async () => { createComponent({ - provide: { - isApolloBoard: true, - }, props: { isCurrentBoardLoading: true, }, @@ -243,7 +224,6 @@ describe('BoardsSelector', () => { ${WORKSPACE_GROUP} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess} ${WORKSPACE_PROJECT} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess} `('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => { - createStore(); createComponent({ isGroupBoard: boardType === WORKSPACE_GROUP, isProjectBoard: boardType === WORKSPACE_PROJECT, @@ -265,7 +245,6 @@ describe('BoardsSelector', () => { ${WORKSPACE_GROUP} ${WORKSPACE_PROJECT} `('sets error when fetching $boardType boards fails', async ({ boardType }) => { - createStore(); createComponent({ isGroupBoard: boardType === WORKSPACE_GROUP, isProjectBoard: boardType === WORKSPACE_PROJECT, @@ -287,7 +266,6 @@ describe('BoardsSelector', () => { describe('dropdown visibility', () => { describe('when multipleIssueBoardsAvailable is enabled', () => { it('show dropdown', () => { - createStore(); createComponent({ provide: { multipleIssueBoardsAvailable: true } }); expect(findDropdown().exists()).toBe(true); expect(findDropdown().props('toggleText')).toBe('Select board'); @@ -296,7 +274,6 @@ describe('BoardsSelector', () => { describe('when multipleIssueBoardsAvailable is disabled but it hasMissingBoards', () => { it('show dropdown', () => { - createStore(); createComponent({ provide: { multipleIssueBoardsAvailable: false, hasMissingBoards: true }, }); @@ -307,7 +284,6 @@ describe('BoardsSelector', () => { describe("when multipleIssueBoardsAvailable is disabled and it dosn't hasMissingBoards", () => { it('hide dropdown', () => { - createStore(); createComponent({ provide: { multipleIssueBoardsAvailable: false, hasMissingBoards: false }, }); @@ -320,7 +296,6 @@ describe('BoardsSelector', () => { it('displays loading state of dropdown while current board is being fetched', () => { createComponent({ props: { isCurrentBoardLoading: true }, - provide: { isApolloBoard: true }, }); expect(findDropdown().props('loading')).toBe(true); expect(findDropdown().props('toggleText')).toBe('Select board'); diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js index 3aaf4b7ae3f..3a5e108ac07 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -332,15 +332,17 @@ export const mockIssue = { confidential: false, referencePath: `${mockIssueFullPath}#27`, path: `/${mockIssueFullPath}/-/issues/27`, - assignees, - labels: [ - { - id: 1, - title: 'test', - color: '#F0AD4E', - description: 'testing', - }, - ], + assignees: { nodes: assignees }, + labels: { + nodes: [ + { + id: 1, + title: 'test', + color: '#F0AD4E', + description: 'testing', + }, + ], + }, epic: { id: 'gid://gitlab/Epic/41', }, @@ -412,6 +414,7 @@ export const mockActiveIssue = { }; export const mockIssue2 = { + ...rawIssue, id: 'gid://gitlab/Issue/437', iid: 28, title: 'Issue 2', @@ -421,14 +424,13 @@ export const mockIssue2 = { confidential: false, referencePath: 'gitlab-org/test-subgroup/gitlab-test#28', path: '/gitlab-org/test-subgroup/gitlab-test/-/issues/28', - assignees, - labels, epic: { id: 'gid://gitlab/Epic/40', }, }; export const mockIssue3 = { + ...rawIssue, id: 'gid://gitlab/Issue/438', iid: 29, title: 'Issue 3', @@ -437,12 +439,11 @@ export const mockIssue3 = { timeEstimate: 0, confidential: false, path: '/gitlab-org/gitlab-test/-/issues/28', - assignees, - labels, epic: null, }; export const mockIssue4 = { + ...rawIssue, id: 'gid://gitlab/Issue/439', iid: 30, title: 'Issue 4', @@ -451,12 +452,11 @@ export const mockIssue4 = { timeEstimate: 0, confidential: false, path: '/gitlab-org/gitlab-test/-/issues/28', - assignees, - labels, epic: null, }; export const mockIssue5 = { + ...rawIssue, id: 'gid://gitlab/Issue/440', iid: 40, title: 'Issue 5', @@ -465,12 +465,11 @@ export const mockIssue5 = { timeEstimate: 0, confidential: false, path: '/gitlab-org/gitlab-test/-/issues/40', - assignees, - labels, epic: null, }; export const mockIssue6 = { + ...rawIssue, id: 'gid://gitlab/Issue/441', iid: 41, title: 'Issue 6', @@ -479,12 +478,11 @@ export const mockIssue6 = { timeEstimate: 0, confidential: false, path: '/gitlab-org/gitlab-test/-/issues/41', - assignees, - labels, epic: null, }; export const mockIssue7 = { + ...rawIssue, id: 'gid://gitlab/Issue/442', iid: 42, title: 'Issue 6', @@ -493,8 +491,6 @@ export const mockIssue7 = { timeEstimate: 0, confidential: false, path: '/gitlab-org/gitlab-test/-/issues/42', - assignees, - labels, epic: null, }; @@ -1086,4 +1082,36 @@ export const mockGroupProjectsResponse = (projects = mockProjects) => ({ }, }); +export const mockGroupIssuesResponse = ( + listId = 'gid://gitlab/List/1', + rawIssues = [rawIssue], +) => ({ + data: { + group: { + id: 'gid://gitlab/Group/1', + board: { + __typename: 'Board', + id: 'gid://gitlab/Board/1', + lists: { + nodes: [ + { + id: listId, + listType: 'backlog', + issues: { + nodes: rawIssues, + pageInfo: { + endCursor: null, + hasNextPage: true, + }, + }, + __typename: 'BoardList', + }, + ], + }, + }, + __typename: 'Group', + }, + }, +}); + export const DEFAULT_COLOR = '#1068bf'; diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js deleted file mode 100644 index 2d68c070b83..00000000000 --- a/spec/frontend/boards/stores/mutations_spec.js +++ /dev/null @@ -1,672 +0,0 @@ -import { cloneDeep } from 'lodash'; -import * as types from '~/boards/stores/mutation_types'; -import mutations from '~/boards/stores/mutations'; -import defaultState from '~/boards/stores/state'; -import { TYPE_ISSUE } from '~/issues/constants'; -import { - mockBoard, - mockLists, - rawIssue, - mockIssue, - mockIssue2, - mockGroupProjects, - labels, - mockList, -} from '../mock_data'; - -describe('Board Store Mutations', () => { - let state; - - const initialBoardListsState = { - 'gid://gitlab/List/1': mockLists[0], - 'gid://gitlab/List/2': mockLists[1], - }; - - const setBoardsListsState = () => { - state = cloneDeep({ - ...state, - boardItemsByListId: { 'gid://gitlab/List/1': [mockIssue.id] }, - boardLists: { 'gid://gitlab/List/1': mockList }, - }); - }; - - beforeEach(() => { - state = defaultState(); - }); - - describe('REQUEST_CURRENT_BOARD', () => { - it('Should set isBoardLoading state to true', () => { - mutations[types.REQUEST_CURRENT_BOARD](state); - - expect(state.isBoardLoading).toBe(true); - }); - }); - - describe('RECEIVE_BOARD_SUCCESS', () => { - it('Should set board to state', () => { - mutations[types.RECEIVE_BOARD_SUCCESS](state, mockBoard); - - expect(state.board).toEqual({ - ...mockBoard, - labels: mockBoard.labels.nodes, - }); - }); - }); - - describe('RECEIVE_BOARD_FAILURE', () => { - it('Should set error in state', () => { - mutations[types.RECEIVE_BOARD_FAILURE](state); - - expect(state.error).toEqual( - 'An error occurred while fetching the board. Please reload the page.', - ); - }); - }); - - describe('SET_INITIAL_BOARD_DATA', () => { - it('Should set initial Boards data to state', () => { - const allowSubEpics = true; - const boardId = 1; - const fullPath = 'gitlab-org'; - const boardType = 'group'; - const disabled = false; - const issuableType = TYPE_ISSUE; - - mutations[types.SET_INITIAL_BOARD_DATA](state, { - allowSubEpics, - boardId, - fullPath, - boardType, - disabled, - issuableType, - }); - - expect(state.allowSubEpics).toBe(allowSubEpics); - expect(state.boardId).toEqual(boardId); - expect(state.fullPath).toEqual(fullPath); - expect(state.boardType).toEqual(boardType); - expect(state.disabled).toEqual(disabled); - expect(state.issuableType).toEqual(issuableType); - }); - }); - - describe('SET_BOARD_CONFIG', () => { - it('Should set board config data o state', () => { - const boardConfig = { - milestoneId: 1, - milestoneTitle: 'Milestone 1', - }; - - mutations[types.SET_BOARD_CONFIG](state, boardConfig); - - expect(state.boardConfig).toEqual(boardConfig); - }); - }); - - describe('RECEIVE_BOARD_LISTS_SUCCESS', () => { - it('Should set boardLists to state', () => { - mutations[types.RECEIVE_BOARD_LISTS_SUCCESS](state, initialBoardListsState); - - expect(state.boardLists).toEqual(initialBoardListsState); - }); - }); - - describe('RECEIVE_BOARD_LISTS_FAILURE', () => { - it('Should set error in state', () => { - mutations[types.RECEIVE_BOARD_LISTS_FAILURE](state); - - expect(state.error).toEqual( - 'An error occurred while fetching the board lists. Please reload the page.', - ); - }); - }); - - describe('SET_ACTIVE_ID', () => { - const expected = { id: 1, sidebarType: '' }; - - beforeEach(() => { - mutations.SET_ACTIVE_ID(state, expected); - }); - - it('updates activeListId to be the value that is passed', () => { - expect(state.activeId).toBe(expected.id); - }); - - it('updates sidebarType to be the value that is passed', () => { - expect(state.sidebarType).toBe(expected.sidebarType); - }); - }); - - describe('SET_FILTERS', () => { - it('updates filterParams to be the value that is passed', () => { - const filterParams = { labelName: 'label' }; - - mutations.SET_FILTERS(state, filterParams); - - expect(state.filterParams).toBe(filterParams); - }); - }); - - describe('CREATE_LIST_FAILURE', () => { - it('sets error message', () => { - mutations.CREATE_LIST_FAILURE(state); - - expect(state.error).toEqual('An error occurred while creating the list. Please try again.'); - }); - }); - - describe('RECEIVE_LABELS_REQUEST', () => { - it('sets labelsLoading on state', () => { - mutations.RECEIVE_LABELS_REQUEST(state); - - expect(state.labelsLoading).toEqual(true); - }); - }); - - describe('RECEIVE_LABELS_SUCCESS', () => { - it('sets labels on state', () => { - mutations.RECEIVE_LABELS_SUCCESS(state, labels); - - expect(state.labels).toEqual(labels); - expect(state.labelsLoading).toEqual(false); - }); - }); - - describe('RECEIVE_LABELS_FAILURE', () => { - it('sets error message', () => { - mutations.RECEIVE_LABELS_FAILURE(state); - - expect(state.error).toEqual( - 'An error occurred while fetching labels. Please reload the page.', - ); - expect(state.labelsLoading).toEqual(false); - }); - }); - - describe('GENERATE_DEFAULT_LISTS_FAILURE', () => { - it('sets error message', () => { - mutations.GENERATE_DEFAULT_LISTS_FAILURE(state); - - expect(state.error).toEqual( - 'An error occurred while generating lists. Please reload the page.', - ); - }); - }); - - describe('RECEIVE_ADD_LIST_SUCCESS', () => { - it('adds list to boardLists state', () => { - mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockLists[0]); - - expect(state.boardLists).toEqual({ - [mockLists[0].id]: mockLists[0], - }); - }); - }); - - describe('MOVE_LISTS', () => { - it('updates the positions of board lists', () => { - state = { - ...state, - boardLists: initialBoardListsState, - }; - - mutations.MOVE_LISTS(state, [ - { - listId: mockLists[0].id, - position: 1, - }, - { - listId: mockLists[1].id, - position: 0, - }, - ]); - - expect(state.boardLists[mockLists[0].id].position).toBe(1); - expect(state.boardLists[mockLists[1].id].position).toBe(0); - }); - }); - - describe('TOGGLE_LIST_COLLAPSED', () => { - it('updates collapsed attribute of list in boardLists state', () => { - const listId = 'gid://gitlab/List/1'; - state = { - ...state, - boardLists: { - [listId]: mockLists[0], - }, - }; - - expect(state.boardLists[listId].collapsed).toEqual(false); - - mutations.TOGGLE_LIST_COLLAPSED(state, { listId, collapsed: true }); - - expect(state.boardLists[listId].collapsed).toEqual(true); - }); - }); - - describe('REMOVE_LIST', () => { - it('removes list from boardLists', () => { - const [list, secondList] = mockLists; - const expected = { - [secondList.id]: secondList, - }; - state = { - ...state, - boardLists: { ...initialBoardListsState }, - }; - - mutations[types.REMOVE_LIST](state, list.id); - - expect(state.boardLists).toEqual(expected); - }); - }); - - describe('REMOVE_LIST_FAILURE', () => { - it('restores lists from backup', () => { - const backupLists = { ...initialBoardListsState }; - - mutations[types.REMOVE_LIST_FAILURE](state, backupLists); - - expect(state.boardLists).toEqual(backupLists); - }); - - it('sets error state', () => { - const backupLists = { ...initialBoardListsState }; - state = { - ...state, - error: undefined, - }; - - mutations[types.REMOVE_LIST_FAILURE](state, backupLists); - - expect(state.error).toEqual('An error occurred while removing the list. Please try again.'); - }); - }); - - describe('RESET_ISSUES', () => { - it('should remove issues from boardItemsByListId state', () => { - const boardItemsByListId = { - 'gid://gitlab/List/1': [mockIssue.id], - }; - - state = { - ...state, - boardItemsByListId, - }; - - mutations[types.RESET_ISSUES](state); - - expect(state.boardItemsByListId).toEqual({ 'gid://gitlab/List/1': [] }); - }); - }); - - describe('REQUEST_ITEMS_FOR_LIST', () => { - const listId = 'gid://gitlab/List/1'; - const boardItemsByListId = { - [listId]: [mockIssue.id], - }; - - it.each` - fetchNext | isLoading | isLoadingMore - ${true} | ${undefined} | ${true} - ${false} | ${true} | ${undefined} - `( - 'sets isLoading to $isLoading and isLoadingMore to $isLoadingMore when fetchNext is $fetchNext', - ({ fetchNext, isLoading, isLoadingMore }) => { - state = { - ...state, - boardItemsByListId, - listsFlags: { - [listId]: {}, - }, - }; - - mutations[types.REQUEST_ITEMS_FOR_LIST](state, { listId, fetchNext }); - - expect(state.listsFlags[listId].isLoading).toBe(isLoading); - expect(state.listsFlags[listId].isLoadingMore).toBe(isLoadingMore); - }, - ); - }); - - describe('RECEIVE_ITEMS_FOR_LIST_SUCCESS', () => { - it('updates boardItemsByListId and issues on state', () => { - const listIssues = { - 'gid://gitlab/List/1': [mockIssue.id], - }; - const issues = { - 1: mockIssue, - }; - - state = { - ...state, - boardItemsByListId: { - 'gid://gitlab/List/1': [], - }, - boardItems: {}, - boardLists: initialBoardListsState, - }; - - const listPageInfo = { - 'gid://gitlab/List/1': { - endCursor: '', - hasNextPage: false, - }, - }; - - mutations.RECEIVE_ITEMS_FOR_LIST_SUCCESS(state, { - listItems: { listData: listIssues, boardItems: issues }, - listPageInfo, - listId: 'gid://gitlab/List/1', - }); - - expect(state.boardItemsByListId).toEqual(listIssues); - expect(state.boardItems).toEqual(issues); - }); - }); - - describe('RECEIVE_ITEMS_FOR_LIST_FAILURE', () => { - it('sets error message', () => { - state = { - ...state, - boardLists: initialBoardListsState, - error: undefined, - }; - - const listId = 'gid://gitlab/List/1'; - - mutations.RECEIVE_ITEMS_FOR_LIST_FAILURE(state, listId); - - expect(state.error).toEqual( - 'An error occurred while fetching the board issues. Please reload the page.', - ); - }); - }); - - describe('UPDATE_BOARD_ITEM_BY_ID', () => { - const issueId = '1'; - const prop = 'id'; - const value = '2'; - const issue = { [issueId]: { id: 1, title: 'Issue' } }; - - beforeEach(() => { - state = { - ...state, - error: undefined, - boardItems: { - ...issue, - }, - }; - }); - - describe('when the issue is in state', () => { - it('updates the property of the correct issue', () => { - mutations.UPDATE_BOARD_ITEM_BY_ID(state, { - itemId: issueId, - prop, - value, - }); - - expect(state.boardItems[issueId]).toEqual({ ...issue[issueId], id: '2' }); - }); - }); - - describe('when the issue is not in state', () => { - it('throws an error', () => { - expect(() => { - mutations.UPDATE_BOARD_ITEM_BY_ID(state, { - itemId: '3', - prop, - value, - }); - }).toThrow(new Error('No issue found.')); - }); - }); - }); - - describe('MUTATE_ISSUE_SUCCESS', () => { - it('updates issue in issues state', () => { - const issues = { - [rawIssue.id]: { id: rawIssue.id }, - }; - - state = { - ...state, - boardItems: issues, - }; - - mutations.MUTATE_ISSUE_SUCCESS(state, { - issue: rawIssue, - }); - - expect(state.boardItems).toEqual({ [mockIssue.id]: mockIssue }); - }); - }); - - describe('UPDATE_BOARD_ITEM', () => { - it('updates the given issue in state.boardItems', () => { - const updatedIssue = { id: 'some_gid', foo: 'bar' }; - state = { boardItems: { some_gid: { id: 'some_gid' } } }; - - mutations.UPDATE_BOARD_ITEM(state, updatedIssue); - - expect(state.boardItems.some_gid).toEqual(updatedIssue); - }); - }); - - describe('REMOVE_BOARD_ITEM', () => { - it('removes the given issue from state.boardItems', () => { - state = { boardItems: { some_gid: {}, some_gid2: {} } }; - - mutations.REMOVE_BOARD_ITEM(state, 'some_gid'); - - expect(state.boardItems).toEqual({ some_gid2: {} }); - }); - }); - - describe('ADD_BOARD_ITEM_TO_LIST', () => { - beforeEach(() => { - setBoardsListsState(); - }); - - it.each([ - [ - 'at position 0 by default', - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - }, - listState: [mockIssue2.id, mockIssue.id], - }, - ], - [ - 'at a given position', - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - atIndex: 1, - }, - listState: [mockIssue.id, mockIssue2.id], - }, - ], - [ - "below the issue with id of 'moveBeforeId'", - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - moveBeforeId: mockIssue.id, - }, - listState: [mockIssue.id, mockIssue2.id], - }, - ], - [ - "above the issue with id of 'moveAfterId'", - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - moveAfterId: mockIssue.id, - }, - listState: [mockIssue2.id, mockIssue.id], - }, - ], - [ - 'to the top of the list', - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - positionInList: 0, - atIndex: 1, - }, - listState: [mockIssue2.id, mockIssue.id], - }, - ], - [ - 'to the bottom of the list when the list is fully loaded', - { - payload: { - itemId: mockIssue2.id, - listId: mockList.id, - positionInList: -1, - atIndex: 0, - allItemsLoadedInList: true, - }, - listState: [mockIssue.id, mockIssue2.id], - }, - ], - ])(`inserts an item into a list %s`, (_, { payload, listState }) => { - mutations.ADD_BOARD_ITEM_TO_LIST(state, payload); - - expect(state.boardItemsByListId[payload.listId]).toEqual(listState); - }); - }); - - describe('REMOVE_BOARD_ITEM_FROM_LIST', () => { - beforeEach(() => { - setBoardsListsState(); - }); - - it('removes an item from a list', () => { - expect(state.boardItemsByListId['gid://gitlab/List/1']).toContain(mockIssue.id); - - mutations.REMOVE_BOARD_ITEM_FROM_LIST(state, { - itemId: mockIssue.id, - listId: mockList.id, - }); - - expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue.id); - }); - }); - - describe('SET_ASSIGNEE_LOADING', () => { - it('sets isSettingAssignees to the value passed', () => { - mutations.SET_ASSIGNEE_LOADING(state, true); - - expect(state.isSettingAssignees).toBe(true); - }); - }); - - describe('REQUEST_GROUP_PROJECTS', () => { - it('Should set isLoading in groupProjectsFlags to true in state when fetchNext is false', () => { - mutations[types.REQUEST_GROUP_PROJECTS](state, false); - - expect(state.groupProjectsFlags.isLoading).toBe(true); - }); - - it('Should set isLoadingMore in groupProjectsFlags to true in state when fetchNext is true', () => { - mutations[types.REQUEST_GROUP_PROJECTS](state, true); - - expect(state.groupProjectsFlags.isLoadingMore).toBe(true); - }); - }); - - describe('RECEIVE_GROUP_PROJECTS_SUCCESS', () => { - it('Should set groupProjects and pageInfo to state and isLoading in groupProjectsFlags to false', () => { - mutations[types.RECEIVE_GROUP_PROJECTS_SUCCESS](state, { - projects: mockGroupProjects, - pageInfo: { hasNextPage: false }, - }); - - expect(state.groupProjects).toEqual(mockGroupProjects); - expect(state.groupProjectsFlags.isLoading).toBe(false); - expect(state.groupProjectsFlags.pageInfo).toEqual({ hasNextPage: false }); - }); - - it('Should merge projects in groupProjects in state when fetchNext is true', () => { - state = { - ...state, - groupProjects: [mockGroupProjects[0]], - }; - - mutations[types.RECEIVE_GROUP_PROJECTS_SUCCESS](state, { - projects: [mockGroupProjects[1]], - fetchNext: true, - }); - - expect(state.groupProjects).toEqual(mockGroupProjects); - }); - }); - - describe('RECEIVE_GROUP_PROJECTS_FAILURE', () => { - it('Should set error in state and isLoading in groupProjectsFlags to false', () => { - mutations[types.RECEIVE_GROUP_PROJECTS_FAILURE](state); - - expect(state.error).toEqual( - 'An error occurred while fetching group projects. Please try again.', - ); - expect(state.groupProjectsFlags.isLoading).toBe(false); - }); - }); - - describe('SET_SELECTED_PROJECT', () => { - it('Should set selectedProject to state', () => { - mutations[types.SET_SELECTED_PROJECT](state, mockGroupProjects[0]); - - expect(state.selectedProject).toEqual(mockGroupProjects[0]); - }); - }); - - describe('ADD_BOARD_ITEM_TO_SELECTION', () => { - it('Should add boardItem to selectedBoardItems state', () => { - expect(state.selectedBoardItems).toEqual([]); - - mutations[types.ADD_BOARD_ITEM_TO_SELECTION](state, mockIssue); - - expect(state.selectedBoardItems).toEqual([mockIssue]); - }); - }); - - describe('REMOVE_BOARD_ITEM_FROM_SELECTION', () => { - it('Should remove boardItem to selectedBoardItems state', () => { - state.selectedBoardItems = [mockIssue]; - - mutations[types.REMOVE_BOARD_ITEM_FROM_SELECTION](state, mockIssue); - - expect(state.selectedBoardItems).toEqual([]); - }); - }); - - describe('RESET_BOARD_ITEM_SELECTION', () => { - it('Should reset selectedBoardItems state', () => { - state.selectedBoardItems = [mockIssue]; - - mutations[types.RESET_BOARD_ITEM_SELECTION](state, mockIssue); - - expect(state.selectedBoardItems).toEqual([]); - }); - }); - - describe('SET_ERROR', () => { - it('Should set error state', () => { - state.error = undefined; - - mutations[types.SET_ERROR](state, 'mayday'); - - expect(state.error).toBe('mayday'); - }); - }); -}); diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js index efe89100e90..74c998bfc51 100644 --- a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js +++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions_spec.js @@ -6,6 +6,7 @@ import { PREREQUISITES_DOC_LINK, OAUTH_SELF_MANAGED_DOC_LINK, SET_UP_INSTANCE_DOC_LINK, + JIRA_USER_REQUIREMENTS_DOC_LINK, } from '~/jira_connect/subscriptions/constants'; import SetupInstructions from '~/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/setup_instructions.vue'; @@ -15,6 +16,7 @@ describe('SetupInstructions', () => { const findPrerequisitesGlLink = () => wrapper.findAllComponents(GlLink).at(0); const findOAuthGlLink = () => wrapper.findAllComponents(GlLink).at(1); const findSetUpInstanceGlLink = () => wrapper.findAllComponents(GlLink).at(2); + const findJiraUserRequirementsGlLink = () => wrapper.findAllComponents(GlLink).at(3); const findBackButton = () => wrapper.findAllComponents(GlButton).at(0); const findNextButton = () => wrapper.findAllComponents(GlButton).at(1); const findCheckboxAtIndex = (index) => wrapper.findAllComponents(GlFormCheckbox).at(index); @@ -40,6 +42,12 @@ describe('SetupInstructions', () => { expect(findSetUpInstanceGlLink().attributes('href')).toBe(SET_UP_INSTANCE_DOC_LINK); }); + it('renders "Jira user requirements" link to documentation', () => { + expect(findJiraUserRequirementsGlLink().attributes('href')).toBe( + JIRA_USER_REQUIREMENTS_DOC_LINK, + ); + }); + describe('NextButton', () => { it('emits next event when clicked and all steps checked', async () => { createComponent(); @@ -47,6 +55,7 @@ describe('SetupInstructions', () => { findCheckboxAtIndex(0).vm.$emit('input', true); findCheckboxAtIndex(1).vm.$emit('input', true); findCheckboxAtIndex(2).vm.$emit('input', true); + findCheckboxAtIndex(3).vm.$emit('input', true); await nextTick(); diff --git a/spec/frontend/ml/model_registry/apps/index_ml_models_spec.js b/spec/frontend/ml/model_registry/apps/index_ml_models_spec.js index 6e0ab2ebe2d..084a747ced4 100644 --- a/spec/frontend/ml/model_registry/apps/index_ml_models_spec.js +++ b/spec/frontend/ml/model_registry/apps/index_ml_models_spec.js @@ -1,7 +1,7 @@ +import { GlBadge } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { IndexMlModels } from '~/ml/model_registry/apps'; import ModelRow from '~/ml/model_registry/components/model_row.vue'; -import { TITLE_LABEL, NO_MODELS_LABEL } from '~/ml/model_registry/translations'; import Pagination from '~/vue_shared/components/incubation/pagination.vue'; import SearchBar from '~/ml/model_registry/components/search_bar.vue'; import { BASE_SORT_FIELDS } from '~/ml/model_registry/constants'; @@ -18,10 +18,11 @@ const createWrapper = ( const findModelRow = (index) => wrapper.findAllComponents(ModelRow).at(index); const findPagination = () => wrapper.findComponent(Pagination); -const findEmptyLabel = () => wrapper.findByText(NO_MODELS_LABEL); +const findEmptyLabel = () => wrapper.findByText('No models registered in this project'); const findSearchBar = () => wrapper.findComponent(SearchBar); const findTitleArea = () => wrapper.findComponent(TitleArea); const findModelCountMetadataItem = () => findTitleArea().findComponent(MetadataItem); +const findBadge = () => wrapper.findComponent(GlBadge); describe('MlModelsIndex', () => { describe('empty state', () => { @@ -51,7 +52,11 @@ describe('MlModelsIndex', () => { describe('header', () => { it('displays the title', () => { - expect(findTitleArea().props('title')).toBe(TITLE_LABEL); + expect(findTitleArea().text()).toContain('Model registry'); + }); + + it('displays the experiment badge', () => { + expect(findBadge().attributes().href).toBe('/help/user/project/ml/model_registry/index.md'); }); it('sets model metadata item to model count', () => { diff --git a/spec/frontend/notes/components/discussion_filter_spec.js b/spec/frontend/notes/components/discussion_filter_spec.js index 87ccb5b7394..dfc901bf1b3 100644 --- a/spec/frontend/notes/components/discussion_filter_spec.js +++ b/spec/frontend/notes/components/discussion_filter_spec.js @@ -6,7 +6,7 @@ import AxiosMockAdapter from 'axios-mock-adapter'; import Vuex from 'vuex'; import { TEST_HOST } from 'helpers/test_constants'; import createEventHub from '~/helpers/event_hub_factory'; - +import * as urlUtility from '~/lib/utils/url_utility'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import DiscussionFilter from '~/notes/components/discussion_filter.vue'; @@ -40,7 +40,7 @@ describe('DiscussionFilter component', () => { const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync); - const mountComponent = () => { + const mountComponent = ({ propsData = {} } = {}) => { const discussions = [ { ...discussionMock, @@ -63,11 +63,12 @@ describe('DiscussionFilter component', () => { store.state.discussions = discussions; - return mount(DiscussionFilter, { + wrapper = mount(DiscussionFilter, { store, propsData: { filters: discussionFiltersMock, selectedValue: DISCUSSION_FILTERS_DEFAULT_VALUE, + ...propsData, }, }); }; @@ -88,7 +89,7 @@ describe('DiscussionFilter component', () => { describe('default', () => { beforeEach(() => { - wrapper = mountComponent(); + mountComponent(); jest.spyOn(store, 'dispatch').mockImplementation(); }); @@ -105,7 +106,7 @@ describe('DiscussionFilter component', () => { describe('when asc', () => { beforeEach(() => { - wrapper = mountComponent(); + mountComponent(); jest.spyOn(store, 'dispatch').mockImplementation(); }); @@ -125,7 +126,7 @@ describe('DiscussionFilter component', () => { describe('when desc', () => { beforeEach(() => { - wrapper = mountComponent(); + mountComponent(); store.state.discussionSortOrder = DESC; jest.spyOn(store, 'dispatch').mockImplementation(); }); @@ -150,7 +151,7 @@ describe('DiscussionFilter component', () => { describe('discussion filter functionality', () => { beforeEach(() => { - wrapper = mountComponent(); + mountComponent(); }); it('renders the all filters', () => { @@ -215,7 +216,7 @@ describe('DiscussionFilter component', () => { currentTab: 'show', }; - wrapper = mountComponent(); + mountComponent(); }); afterEach(() => { @@ -239,7 +240,7 @@ describe('DiscussionFilter component', () => { it('does not update the filter when the current filter is "Show all activity"', async () => { window.location.hash = `note_${discussionMock.notes[0].id}`; - wrapper = mountComponent(); + mountComponent(); await nextTick(); const filtered = findGlDisclosureDropdownItems().filter((el) => el.classes('is-active')); @@ -250,7 +251,7 @@ describe('DiscussionFilter component', () => { it('only updates filter when the URL links to a note', async () => { window.location.hash = `testing123`; - wrapper = mountComponent(); + mountComponent(); await nextTick(); const filtered = findGlDisclosureDropdownItems().filter((el) => el.classes('is-active')); @@ -260,12 +261,32 @@ describe('DiscussionFilter component', () => { }); it('does not fetch discussions when there is no hash', async () => { - window.location.hash = ''; - const selectFilterSpy = jest.spyOn(wrapper.vm, 'selectFilter').mockImplementation(() => {}); - wrapper = mountComponent(); + mountComponent(); + const dispatchSpy = jest.spyOn(store, 'dispatch'); await nextTick(); - expect(selectFilterSpy).not.toHaveBeenCalled(); + expect(dispatchSpy).not.toHaveBeenCalled(); + }); + + describe('selected value is not default state', () => { + beforeEach(() => { + mountComponent({ + propsData: { selectedValue: 2 }, + }); + }); + it('fetch discussions when there is hash', async () => { + jest.spyOn(urlUtility, 'getLocationHash').mockReturnValueOnce('note_123'); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + window.dispatchEvent(new Event('hashchange')); + + await nextTick(); + expect(dispatchSpy).toHaveBeenCalledWith('filterDiscussion', { + filter: 0, + path: 'http://test.host/example', + persistFilter: false, + }); + }); }); }); }); -- cgit v1.2.3