Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/boards/components')
-rw-r--r--spec/frontend/boards/components/board_add_new_column_form_spec.js1
-rw-r--r--spec/frontend/boards/components/board_add_new_column_spec.js66
-rw-r--r--spec/frontend/boards/components/board_add_new_column_trigger_spec.js1
-rw-r--r--spec/frontend/boards/components/board_app_spec.js26
-rw-r--r--spec/frontend/boards/components/board_card_move_to_position_spec.js1
-rw-r--r--spec/frontend/boards/components/board_card_spec.js1
-rw-r--r--spec/frontend/boards/components/board_content_sidebar_spec.js1
-rw-r--r--spec/frontend/boards/components/board_content_spec.js65
-rw-r--r--spec/frontend/boards/components/board_filtered_search_spec.js1
-rw-r--r--spec/frontend/boards/components/board_form_spec.js1
-rw-r--r--spec/frontend/boards/components/board_list_header_spec.js57
-rw-r--r--spec/frontend/boards/components/board_new_issue_spec.js1
-rw-r--r--spec/frontend/boards/components/board_settings_sidebar_spec.js34
-rw-r--r--spec/frontend/boards/components/board_top_bar_spec.js41
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js38
-rw-r--r--spec/frontend/boards/components/config_toggle_spec.js21
-rw-r--r--spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js67
17 files changed, 322 insertions, 101 deletions
diff --git a/spec/frontend/boards/components/board_add_new_column_form_spec.js b/spec/frontend/boards/components/board_add_new_column_form_spec.js
index 35296f36b89..719e36629c2 100644
--- a/spec/frontend/boards/components/board_add_new_column_form_spec.js
+++ b/spec/frontend/boards/components/board_add_new_column_form_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form.vue';
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 8d6cc9373af..1a847d35900 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,17 @@
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';
import {
mockLabelList,
createBoardListResponse,
@@ -21,13 +24,14 @@ Vue.use(VueApollo);
describe('BoardAddNewColumn', () => {
let wrapper;
+ let mockApollo;
const createBoardListQueryHandler = jest.fn().mockResolvedValue(createBoardListResponse);
const labelsQueryHandler = jest.fn().mockResolvedValue(labelsQueryResponse);
- const mockApollo = createMockApollo([
- [boardLabelsQuery, labelsQueryHandler],
- [createBoardListMutation, createBoardListQueryHandler],
- ]);
+ const errorMessage = 'Failed to create list';
+ const createBoardListQueryHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
+ const errorMessageLabels = 'Failed to fetch labels';
+ const labelsQueryHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessageLabels));
const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
const findAddNewColumnForm = () => wrapper.findComponent(BoardAddNewColumnForm);
@@ -53,7 +57,14 @@ describe('BoardAddNewColumn', () => {
actions = {},
provide = {},
lists = {},
+ labelsHandler = labelsQueryHandler,
+ createHandler = createBoardListQueryHandler,
} = {}) => {
+ mockApollo = createMockApollo([
+ [boardLabelsQuery, labelsHandler],
+ [createBoardListMutation, createHandler],
+ ]);
+
wrapper = shallowMountExtended(BoardAddNewColumn, {
apolloProvider: mockApollo,
propsData: {
@@ -111,6 +122,10 @@ describe('BoardAddNewColumn', () => {
mockApollo.clients.defaultClient.cache.writeQuery = jest.fn();
};
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
+
describe('Add list button', () => {
it('calls addList', async () => {
const getListByLabelId = jest.fn().mockReturnValue(null);
@@ -208,11 +223,52 @@ describe('BoardAddNewColumn', () => {
findAddNewColumnForm().vm.$emit('add-list');
- await nextTick();
+ await waitForPromises();
expect(wrapper.emitted('highlight-list')).toEqual([[mockLabelList.id]]);
expect(createBoardListQueryHandler).not.toHaveBeenCalledWith();
});
});
+
+ describe('when fetch labels query fails', () => {
+ beforeEach(() => {
+ mountComponent({
+ provide: { isApolloBoard: true },
+ labelsHandler: labelsQueryHandlerFailure,
+ });
+ });
+
+ it('sets error', async () => {
+ findDropdown().vm.$emit('show');
+
+ await waitForPromises();
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
+ });
+
+ describe('when create list mutation fails', () => {
+ beforeEach(() => {
+ mountComponent({
+ selectedId: mockLabelList.label.id,
+ provide: { isApolloBoard: true },
+ createHandler: createBoardListQueryHandlerFailure,
+ });
+ });
+
+ it('sets error', async () => {
+ findDropdown().vm.$emit('show');
+
+ await nextTick();
+ expect(labelsQueryHandler).toHaveBeenCalled();
+
+ selectLabel(mockLabelList.label.id);
+
+ findAddNewColumnForm().vm.$emit('add-list');
+
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
+ });
});
});
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 825cfc9453a..396ec7d67cd 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,6 @@
import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
diff --git a/spec/frontend/boards/components/board_app_spec.js b/spec/frontend/boards/components/board_app_spec.js
index e7624437ac5..b16f9b26f40 100644
--- a/spec/frontend/boards/components/board_app_spec.js
+++ b/spec/frontend/boards/components/board_app_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
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';
@@ -9,13 +10,17 @@ import BoardApp from '~/boards/components/board_app.vue';
import eventHub from '~/boards/eventhub';
import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql';
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
+import * as cacheUpdates from '~/boards/graphql/cache_updates';
import { rawIssue, boardListsQueryResponse } from '../mock_data';
describe('BoardApp', () => {
let wrapper;
let store;
+ let mockApollo;
+
+ const errorMessage = 'Failed to fetch lists';
const boardListQueryHandler = jest.fn().mockResolvedValue(boardListsQueryResponse);
- const mockApollo = createMockApollo([[boardListsQuery, boardListQueryHandler]]);
+ const boardListQueryHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
Vue.use(Vuex);
Vue.use(VueApollo);
@@ -33,7 +38,12 @@ describe('BoardApp', () => {
});
};
- const createComponent = ({ isApolloBoard = false, issue = rawIssue } = {}) => {
+ const createComponent = ({
+ isApolloBoard = false,
+ issue = rawIssue,
+ handler = boardListQueryHandler,
+ } = {}) => {
+ mockApollo = createMockApollo([[boardListsQuery, handler]]);
mockApollo.clients.defaultClient.cache.writeQuery({
query: activeBoardItemQuery,
data: {
@@ -57,6 +67,10 @@ describe('BoardApp', () => {
});
};
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
+
afterEach(() => {
store = null;
});
@@ -104,5 +118,13 @@ describe('BoardApp', () => {
expect(eventHub.$on).toHaveBeenCalledWith('updateBoard', wrapper.vm.refetchLists);
});
+
+ it('sets error on fetch lists failure', async () => {
+ createComponent({ isApolloBoard: true, handler: boardListQueryHandlerFailure });
+
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js
index 5f308be5580..20beaf2e9bd 100644
--- a/spec/frontend/boards/components/board_card_move_to_position_spec.js
+++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
import {
diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js
index 897219303b5..167efb94fcc 100644
--- a/spec/frontend/boards/components/board_card_spec.js
+++ b/spec/frontend/boards/components/board_card_spec.js
@@ -1,5 +1,6 @@
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';
diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js
index 9be2696de56..01eea12bf0a 100644
--- a/spec/frontend/boards/components/board_content_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_content_sidebar_spec.js
@@ -2,6 +2,7 @@ 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';
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
index 0a2a78479fb..675b79a8b1a 100644
--- a/spec/frontend/boards/components/board_content_spec.js
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -1,8 +1,9 @@
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import Draggable from 'vuedraggable';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -10,6 +11,7 @@ import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
import getters from 'ee_else_ce/boards/stores/getters';
+import * as cacheUpdates from '~/boards/graphql/cache_updates';
import BoardColumn from '~/boards/components/board_column.vue';
import BoardContent from '~/boards/components/board_content.vue';
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
@@ -36,6 +38,8 @@ describe('BoardContent', () => {
let mockApollo;
const updateListHandler = jest.fn().mockResolvedValue(updateBoardListResponse);
+ const errorMessage = 'Failed to update list';
+ const updateListHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
const defaultState = {
isShowingEpicsSwimlanes: false,
@@ -60,8 +64,9 @@ describe('BoardContent', () => {
issuableType = 'issue',
isIssueBoard = true,
isEpicBoard = false,
+ handler = updateListHandler,
} = {}) => {
- mockApollo = createMockApollo([[updateBoardListMutation, updateListHandler]]);
+ mockApollo = createMockApollo([[updateBoardListMutation, handler]]);
const listQueryVariables = { isProject: true };
mockApollo.clients.defaultClient.writeQuery({
@@ -107,6 +112,11 @@ describe('BoardContent', () => {
const findBoardColumns = () => wrapper.findAllComponents(BoardColumn);
const findBoardAddNewColumn = () => wrapper.findComponent(BoardAddNewColumn);
const findDraggable = () => wrapper.findComponent(Draggable);
+ const findError = () => wrapper.findComponent(GlAlert);
+
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
describe('default', () => {
beforeEach(() => {
@@ -123,7 +133,7 @@ describe('BoardContent', () => {
it('does not display EpicsSwimlanes component', () => {
expect(wrapper.findComponent(EpicsSwimlanes).exists()).toBe(false);
- expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
+ expect(findError().exists()).toBe(false);
});
it('sets delay and delayOnTouchOnly attributes on board list', () => {
@@ -169,6 +179,18 @@ describe('BoardContent', () => {
});
describe('when Apollo boards FF is on', () => {
+ const moveList = () => {
+ const movableListsOrder = [mockLists[0].id, mockLists[1].id];
+
+ findDraggable().vm.$emit('end', {
+ item: { dataset: { listId: mockLists[0].id, draggableItemType: DraggableItemTypes.list } },
+ newIndex: 1,
+ to: {
+ children: movableListsOrder.map((listId) => ({ dataset: { listId } })),
+ },
+ });
+ };
+
beforeEach(async () => {
createComponent({ isApolloBoard: true });
await waitForPromises();
@@ -183,19 +205,38 @@ describe('BoardContent', () => {
});
it('reorders lists', async () => {
- const movableListsOrder = [mockLists[0].id, mockLists[1].id];
-
- findDraggable().vm.$emit('end', {
- item: { dataset: { listId: mockLists[0].id, draggableItemType: DraggableItemTypes.list } },
- newIndex: 1,
- to: {
- children: movableListsOrder.map((listId) => ({ dataset: { listId } })),
- },
- });
+ moveList();
await waitForPromises();
expect(updateListHandler).toHaveBeenCalled();
});
+
+ it('sets error on reorder lists failure', async () => {
+ createComponent({ isApolloBoard: true, handler: updateListHandlerFailure });
+
+ moveList();
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
+
+ describe('when error is passed', () => {
+ beforeEach(async () => {
+ createComponent({ isApolloBoard: true, props: { apolloError: 'Error' } });
+ await waitForPromises();
+ });
+
+ it('displays error banner', () => {
+ expect(findError().exists()).toBe(true);
+ });
+
+ it('dismisses error', async () => {
+ findError().vm.$emit('dismiss');
+ await nextTick();
+
+ expect(cacheUpdates.setError).toHaveBeenCalledWith({ message: null, captureError: false });
+ });
+ });
});
describe('when "add column" form is visible', () => {
diff --git a/spec/frontend/boards/components/board_filtered_search_spec.js b/spec/frontend/boards/components/board_filtered_search_spec.js
index 5a976816f74..0bd936c9abd 100644
--- a/spec/frontend/boards/components/board_filtered_search_spec.js
+++ b/spec/frontend/boards/components/board_filtered_search_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
import { updateHistory } from '~/lib/utils/url_utility';
diff --git a/spec/frontend/boards/components/board_form_spec.js b/spec/frontend/boards/components/board_form_spec.js
index 5604c589e37..15ee3976fb1 100644
--- a/spec/frontend/boards/components/board_form_spec.js
+++ b/spec/frontend/boards/components/board_form_spec.js
@@ -1,5 +1,6 @@
import { GlModal } from '@gitlab/ui';
import Vue from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import VueApollo from 'vue-apollo';
import setWindowLocation from 'helpers/set_window_location_helper';
diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js
index 0c9e1b4646e..76e969f1725 100644
--- a/spec/frontend/boards/components/board_list_header_spec.js
+++ b/spec/frontend/boards/components/board_list_header_spec.js
@@ -1,9 +1,11 @@
import { GlButtonGroup } 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 { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import {
boardListQueryResponse,
mockLabelList,
@@ -12,6 +14,7 @@ import {
import BoardListHeader from '~/boards/components/board_list_header.vue';
import updateBoardListMutation from '~/boards/graphql/board_list_update.mutation.graphql';
import { ListType } from '~/boards/constants';
+import * as cacheUpdates from '~/boards/graphql/cache_updates';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
Vue.use(VueApollo);
@@ -25,7 +28,11 @@ describe('Board List Header Component', () => {
const updateListSpy = jest.fn();
const toggleListCollapsedSpy = jest.fn();
const mockClientToggleListCollapsedResolver = jest.fn();
- const updateListHandler = jest.fn().mockResolvedValue(updateBoardListResponse);
+ const updateListHandlerSuccess = jest.fn().mockResolvedValue(updateBoardListResponse);
+
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
afterEach(() => {
fakeApollo = null;
@@ -39,6 +46,7 @@ describe('Board List Header Component', () => {
withLocalStorage = true,
currentUserId = 1,
listQueryHandler = jest.fn().mockResolvedValue(boardListQueryResponse()),
+ updateListHandler = updateListHandlerSuccess,
injectedProps = {},
} = {}) => {
const boardId = 'gid://gitlab/Board/1';
@@ -271,7 +279,7 @@ describe('Board List Header Component', () => {
findCaret().vm.$emit('click');
await nextTick();
- expect(updateListHandler).not.toHaveBeenCalled();
+ expect(updateListHandlerSuccess).not.toHaveBeenCalled();
});
it('calls update list mutation when user is logged in', async () => {
@@ -280,7 +288,50 @@ describe('Board List Header Component', () => {
findCaret().vm.$emit('click');
await nextTick();
- expect(updateListHandler).toHaveBeenCalledWith({ listId: mockLabelList.id, collapsed: true });
+ expect(updateListHandlerSuccess).toHaveBeenCalledWith({
+ listId: mockLabelList.id,
+ collapsed: true,
+ });
+ });
+
+ describe('when fetch list query fails', () => {
+ const errorMessage = 'Failed to fetch list';
+ const listQueryHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
+
+ beforeEach(() => {
+ createComponent({
+ listQueryHandler: listQueryHandlerFailure,
+ injectedProps: { isApolloBoard: true },
+ });
+ });
+
+ it('sets error', async () => {
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
+ });
+
+ describe('when update list mutation fails', () => {
+ const errorMessage = 'Failed to update list';
+ const updateListHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
+
+ beforeEach(() => {
+ createComponent({
+ currentUserId: 1,
+ updateListHandler: updateListHandlerFailure,
+ injectedProps: { isApolloBoard: true },
+ });
+ });
+
+ it('sets error', async () => {
+ await waitForPromises();
+
+ findCaret().vm.$emit('click');
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
});
});
});
diff --git a/spec/frontend/boards/components/board_new_issue_spec.js b/spec/frontend/boards/components/board_new_issue_spec.js
index a1088f1e8f7..bf2608d0594 100644
--- a/spec/frontend/boards/components/board_new_issue_spec.js
+++ b/spec/frontend/boards/components/board_new_issue_spec.js
@@ -1,5 +1,6 @@
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
diff --git a/spec/frontend/boards/components/board_settings_sidebar_spec.js b/spec/frontend/boards/components/board_settings_sidebar_spec.js
index affe1260c66..f6ed483dfc5 100644
--- a/spec/frontend/boards/components/board_settings_sidebar_spec.js
+++ b/spec/frontend/boards/components/board_settings_sidebar_spec.js
@@ -3,14 +3,17 @@ 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';
@@ -31,12 +34,17 @@ describe('BoardSettingsSidebar', () => {
const destroyBoardListMutationHandlerSuccess = jest
.fn()
.mockResolvedValue(destroyBoardListMutationResponse);
+ const errorMessage = 'Failed to delete list';
+ const destroyBoardListMutationHandlerFailure = jest
+ .fn()
+ .mockRejectedValue(new Error(errorMessage));
const createComponent = ({
canAdminList = false,
list = {},
sidebarType = LIST,
activeId = inactiveId,
+ destroyBoardListMutationHandler = destroyBoardListMutationHandlerSuccess,
isApolloBoard = false,
} = {}) => {
const boardLists = {
@@ -49,9 +57,7 @@ describe('BoardSettingsSidebar', () => {
actions,
});
- mockApollo = createMockApollo([
- [destroyBoardListMutation, destroyBoardListMutationHandlerSuccess],
- ]);
+ mockApollo = createMockApollo([[destroyBoardListMutation, destroyBoardListMutationHandler]]);
wrapper = extendedWrapper(
shallowMount(BoardSettingsSidebar, {
@@ -90,6 +96,10 @@ describe('BoardSettingsSidebar', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findRemoveButton = () => wrapper.findComponent(GlButton);
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
+
it('finds a MountingPortal component', () => {
createComponent();
@@ -214,5 +224,23 @@ describe('BoardSettingsSidebar', () => {
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');
+
+ wrapper.findComponent(GlModal).vm.$emit('primary');
+
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/boards/components/board_top_bar_spec.js b/spec/frontend/boards/components/board_top_bar_spec.js
index afc7da97617..87abe630688 100644
--- a/spec/frontend/boards/components/board_top_bar_spec.js
+++ b/spec/frontend/boards/components/board_top_bar_spec.js
@@ -1,8 +1,10 @@
import { shallowMount } from '@vue/test-utils';
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 BoardTopBar from '~/boards/components/board_top_bar.vue';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
@@ -11,6 +13,7 @@ import ConfigToggle from '~/boards/components/config_toggle.vue';
import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
import NewBoardButton from '~/boards/components/new_board_button.vue';
import ToggleFocus from '~/boards/components/toggle_focus.vue';
+import * as cacheUpdates from '~/boards/graphql/cache_updates';
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import groupBoardQuery from '~/boards/graphql/group_board.query.graphql';
@@ -32,12 +35,18 @@ describe('BoardTopBar', () => {
const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse);
const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse);
-
- const createComponent = ({ provide = {} } = {}) => {
+ const errorMessage = 'Failed to fetch board';
+ const boardQueryHandlerFailure = jest.fn().mockRejectedValue(new Error(errorMessage));
+
+ const createComponent = ({
+ provide = {},
+ projectBoardQueryHandler = projectBoardQueryHandlerSuccess,
+ groupBoardQueryHandler = groupBoardQueryHandlerSuccess,
+ } = {}) => {
const store = createStore();
mockApollo = createMockApollo([
- [projectBoardQuery, projectBoardQueryHandlerSuccess],
- [groupBoardQuery, groupBoardQueryHandlerSuccess],
+ [projectBoardQuery, projectBoardQueryHandler],
+ [groupBoardQuery, groupBoardQueryHandler],
]);
wrapper = shallowMount(BoardTopBar, {
@@ -65,6 +74,10 @@ describe('BoardTopBar', () => {
});
};
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
+
afterEach(() => {
mockApollo = null;
});
@@ -134,5 +147,25 @@ describe('BoardTopBar', () => {
expect(queryHandler).toHaveBeenCalled();
expect(notCalledHandler).not.toHaveBeenCalled();
});
+
+ it.each`
+ boardType
+ ${WORKSPACE_GROUP}
+ ${WORKSPACE_PROJECT}
+ `('sets error when $boardType board query fails', async ({ boardType }) => {
+ createComponent({
+ provide: {
+ boardType,
+ isProjectBoard: boardType === WORKSPACE_PROJECT,
+ isGroupBoard: boardType === WORKSPACE_GROUP,
+ isApolloBoard: true,
+ },
+ groupBoardQueryHandler: boardQueryHandlerFailure,
+ projectBoardQueryHandler: boardQueryHandlerFailure,
+ });
+
+ await waitForPromises();
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 13c017706ef..b17a5589c07 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -1,6 +1,7 @@
import { GlDropdown, GlLoadingIcon, GlDropdownSectionHeader } 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';
@@ -9,6 +10,7 @@ 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 * as cacheUpdates from '~/boards/graphql/cache_updates';
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
@@ -37,7 +39,6 @@ describe('BoardsSelector', () => {
const createStore = () => {
store = new Vuex.Store({
actions: {
- setError: jest.fn(),
setBoardConfig: jest.fn(),
},
state: {
@@ -77,16 +78,19 @@ describe('BoardsSelector', () => {
.fn()
.mockResolvedValue(mockEmptyProjectRecentBoardsResponse);
+ const boardsHandlerFailure = jest.fn().mockRejectedValue(new Error('error'));
+
const createComponent = ({
projectBoardsQueryHandler = projectBoardsQueryHandlerSuccess,
projectRecentBoardsQueryHandler = projectRecentBoardsQueryHandlerSuccess,
+ groupBoardsQueryHandler = groupBoardsQueryHandlerSuccess,
isGroupBoard = false,
isProjectBoard = false,
provide = {},
} = {}) => {
fakeApollo = createMockApollo([
[projectBoardsQuery, projectBoardsQueryHandler],
- [groupBoardsQuery, groupBoardsQueryHandlerSuccess],
+ [groupBoardsQuery, groupBoardsQueryHandler],
[projectRecentBoardsQuery, projectRecentBoardsQueryHandler],
[groupRecentBoardsQuery, groupRecentBoardsQueryHandlerSuccess],
]);
@@ -115,6 +119,10 @@ describe('BoardsSelector', () => {
});
};
+ beforeEach(() => {
+ cacheUpdates.setError = jest.fn();
+ });
+
afterEach(() => {
fakeApollo = null;
});
@@ -173,8 +181,7 @@ describe('BoardsSelector', () => {
it('shows only matching boards when filtering', async () => {
const filterTerm = 'board1';
- const expectedCount = boards.filter((board) => board.node.name.includes(filterTerm))
- .length;
+ const expectedCount = boards.filter((board) => board.name.includes(filterTerm)).length;
fillSearchBox(filterTerm);
@@ -246,6 +253,29 @@ describe('BoardsSelector', () => {
expect(queryHandler).toHaveBeenCalled();
expect(notCalledHandler).not.toHaveBeenCalled();
});
+
+ it.each`
+ boardType
+ ${WORKSPACE_GROUP}
+ ${WORKSPACE_PROJECT}
+ `('sets error when fetching $boardType boards fails', async ({ boardType }) => {
+ createStore();
+ createComponent({
+ isGroupBoard: boardType === WORKSPACE_GROUP,
+ isProjectBoard: boardType === WORKSPACE_PROJECT,
+ projectBoardsQueryHandler: boardsHandlerFailure,
+ groupBoardsQueryHandler: boardsHandlerFailure,
+ });
+
+ await nextTick();
+
+ // Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
+ findDropdown().vm.$emit('show');
+
+ await waitForPromises();
+
+ expect(cacheUpdates.setError).toHaveBeenCalled();
+ });
});
describe('dropdown visibility', () => {
diff --git a/spec/frontend/boards/components/config_toggle_spec.js b/spec/frontend/boards/components/config_toggle_spec.js
index 5330721451e..3d505038331 100644
--- a/spec/frontend/boards/components/config_toggle_spec.js
+++ b/spec/frontend/boards/components/config_toggle_spec.js
@@ -1,7 +1,9 @@
+// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
import ConfigToggle from '~/boards/components/config_toggle.vue';
import eventHub from '~/boards/eventhub';
import store from '~/boards/stores';
@@ -12,13 +14,14 @@ describe('ConfigToggle', () => {
Vue.use(Vuex);
- const createComponent = (provide = {}) =>
+ const createComponent = (provide = {}, props = {}) =>
shallowMount(ConfigToggle, {
store,
provide: {
canAdminList: true,
...provide,
},
+ propsData: props,
});
const findButton = () => wrapper.findComponent(GlButton);
@@ -52,4 +55,20 @@ describe('ConfigToggle', () => {
label: 'edit_board',
});
});
+
+ it.each`
+ boardHasScope
+ ${true}
+ ${false}
+ `('renders dot highlight and tooltip depending on boardHasScope prop', ({ boardHasScope }) => {
+ wrapper = createComponent({}, { boardHasScope });
+
+ expect(findButton().classes('dot-highlight')).toBe(boardHasScope);
+
+ if (boardHasScope) {
+ expect(findButton().attributes('title')).toBe(__("This board's scope is reduced"));
+ } else {
+ expect(findButton().attributes('title')).toBe('');
+ }
+ });
});
diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js
deleted file mode 100644
index b01ee01120e..00000000000
--- a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- To avoid duplicating tests in time_tracker.spec,
- this spec only contains a simple test to check rendering.
-
- A detailed feature spec is used to test time tracking feature
- in swimlanes sidebar.
-*/
-
-import { shallowMount } from '@vue/test-utils';
-import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
-import { createStore } from '~/boards/stores';
-import IssuableTimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
-
-describe('BoardSidebarTimeTracker', () => {
- let wrapper;
- let store;
-
- const createComponent = (options) => {
- wrapper = shallowMount(BoardSidebarTimeTracker, {
- store,
- ...options,
- });
- };
-
- beforeEach(() => {
- store = createStore();
- store.state.boardItems = {
- 1: {
- id: 1,
- iid: 1,
- timeEstimate: 3600,
- totalTimeSpent: 1800,
- humanTimeEstimate: '1h',
- humanTotalTimeSpent: '30min',
- },
- };
- store.state.activeId = '1';
- });
-
- it.each`
- timeTrackingLimitToHours | canUpdate
- ${true} | ${false}
- ${true} | ${true}
- ${false} | ${false}
- ${false} | ${true}
- `(
- 'renders IssuableTimeTracker with correct spent and estimated time (timeTrackingLimitToHours=$timeTrackingLimitToHours, canUpdate=$canUpdate)',
- ({ timeTrackingLimitToHours, canUpdate }) => {
- createComponent({ provide: { timeTrackingLimitToHours, canUpdate } });
-
- expect(wrapper.findComponent(IssuableTimeTracker).props()).toEqual({
- limitToHours: timeTrackingLimitToHours,
- canAddTimeEntries: canUpdate,
- showCollapsed: false,
- issuableId: '1',
- issuableIid: '1',
- fullPath: '',
- initialTimeTracking: {
- timeEstimate: 3600,
- totalTimeSpent: 1800,
- humanTimeEstimate: '1h',
- humanTotalTimeSpent: '30min',
- },
- });
- },
- );
-});