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/stores')
-rw-r--r--spec/frontend/boards/stores/actions_spec.js708
-rw-r--r--spec/frontend/boards/stores/getters_spec.js41
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js239
3 files changed, 685 insertions, 303 deletions
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 69d2c8977fb..460e77a3f03 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -1,16 +1,21 @@
+import * as Sentry from '@sentry/browser';
+import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import testAction from 'helpers/vuex_action_helper';
import {
fullBoardId,
formatListIssues,
formatBoardLists,
formatIssueInput,
+ formatIssue,
+ getMoveData,
} from '~/boards/boards_util';
-import { inactiveId, ISSUABLE } from '~/boards/constants';
+import { inactiveId, ISSUABLE, ListType } from '~/boards/constants';
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '~/boards/graphql/issue_create.mutation.graphql';
-import issueMoveListMutation from '~/boards/graphql/issue_move_list.mutation.graphql';
import actions, { gqlClient } from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
import {
mockLists,
mockListsById,
@@ -22,6 +27,9 @@ import {
labels,
mockActiveIssue,
mockGroupProjects,
+ mockMoveIssueParams,
+ mockMoveState,
+ mockMoveData,
} from '../mock_data';
jest.mock('~/flash');
@@ -638,73 +646,314 @@ describe('resetIssues', () => {
});
describe('moveItem', () => {
- it('should dispatch moveIssue action', () => {
+ it('should dispatch moveIssue action with payload', () => {
+ const payload = { mock: 'payload' };
+
testAction({
action: actions.moveItem,
- expectedActions: [{ type: 'moveIssue' }],
+ payload,
+ expectedActions: [{ type: 'moveIssue', payload }],
});
});
});
describe('moveIssue', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [436, 437],
- 'gid://gitlab/List/2': [],
- };
-
- const issues = {
- 436: mockIssue,
- 437: mockIssue2,
- };
-
- const state = {
- fullPath: 'gitlab-org',
- boardId: '1',
- boardType: 'group',
- disabled: false,
- boardLists: mockLists,
- boardItemsByListId: listIssues,
- boardItems: issues,
- };
+ it('should dispatch a correct set of actions', () => {
+ testAction({
+ action: actions.moveIssue,
+ payload: mockMoveIssueParams,
+ state: mockMoveState,
+ expectedActions: [
+ { type: 'moveIssueCard', payload: mockMoveData },
+ { type: 'updateMovedIssue', payload: mockMoveData },
+ { type: 'updateIssueOrder', payload: { moveData: mockMoveData } },
+ ],
+ });
+ });
+});
- it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_SUCCESS mutation when successful', (done) => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: {
- issueMoveList: {
- issue: rawIssue,
- errors: [],
+describe('moveIssueCard and undoMoveIssueCard', () => {
+ describe('card should move without clonning', () => {
+ let state;
+ let params;
+ let moveMutations;
+ let undoMutations;
+
+ describe('when re-ordering card', () => {
+ beforeEach(
+ ({
+ itemId = 123,
+ fromListId = 'gid://gitlab/List/1',
+ toListId = 'gid://gitlab/List/1',
+ originalIssue = { foo: 'bar' },
+ originalIndex = 0,
+ moveBeforeId = undefined,
+ moveAfterId = undefined,
+ } = {}) => {
+ state = {
+ boardLists: {
+ [toListId]: { listType: ListType.backlog },
+ [fromListId]: { listType: ListType.backlog },
+ },
+ boardItems: { [itemId]: originalIssue },
+ boardItemsByListId: { [fromListId]: [123] },
+ };
+ params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId };
+ moveMutations = [
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: toListId, moveBeforeId, moveAfterId },
+ },
+ ];
+ undoMutations = [
+ { type: types.UPDATE_BOARD_ITEM, payload: originalIssue },
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: fromListId, atIndex: originalIndex },
+ },
+ ];
},
- },
+ );
+
+ it('moveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.moveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: moveMutations,
+ });
+ });
+
+ it('undoMoveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.undoMoveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: undoMutations,
+ });
+ });
});
- testAction(
- actions.moveIssue,
- {
- itemId: '436',
- itemIid: mockIssue.iid,
- itemPath: mockIssue.referencePath,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- },
- state,
+ describe.each([
[
+ 'issue moves out of backlog',
{
- type: types.MOVE_ISSUE,
- payload: {
- originalIssue: mockIssue,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- },
+ fromListType: ListType.backlog,
+ toListType: ListType.label,
},
+ ],
+ [
+ 'issue card moves to closed',
{
- type: types.MOVE_ISSUE_SUCCESS,
- payload: { issue: rawIssue },
+ fromListType: ListType.label,
+ toListType: ListType.closed,
},
],
- [],
- done,
- );
+ [
+ 'issue card moves to non-closed, non-backlog list of the same type',
+ {
+ fromListType: ListType.label,
+ toListType: ListType.label,
+ },
+ ],
+ ])('when %s', (_, { toListType, fromListType }) => {
+ beforeEach(
+ ({
+ itemId = 123,
+ fromListId = 'gid://gitlab/List/1',
+ toListId = 'gid://gitlab/List/2',
+ originalIssue = { foo: 'bar' },
+ originalIndex = 0,
+ moveBeforeId = undefined,
+ moveAfterId = undefined,
+ } = {}) => {
+ state = {
+ boardLists: {
+ [fromListId]: { listType: fromListType },
+ [toListId]: { listType: toListType },
+ },
+ boardItems: { [itemId]: originalIssue },
+ boardItemsByListId: { [fromListId]: [123], [toListId]: [] },
+ };
+ params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId };
+ moveMutations = [
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: toListId, moveBeforeId, moveAfterId },
+ },
+ ];
+ undoMutations = [
+ { type: types.UPDATE_BOARD_ITEM, payload: originalIssue },
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: toListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: fromListId, atIndex: originalIndex },
+ },
+ ];
+ },
+ );
+
+ it('moveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.moveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: moveMutations,
+ });
+ });
+
+ it('undoMoveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.undoMoveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: undoMutations,
+ });
+ });
+ });
+ });
+
+ describe('card should clone on move', () => {
+ let state;
+ let params;
+ let moveMutations;
+ let undoMutations;
+
+ describe.each([
+ [
+ 'issue card moves to non-closed, non-backlog list of a different type',
+ {
+ fromListType: ListType.label,
+ toListType: ListType.assignee,
+ },
+ ],
+ ])('when %s', (_, { toListType, fromListType }) => {
+ beforeEach(
+ ({
+ itemId = 123,
+ fromListId = 'gid://gitlab/List/1',
+ toListId = 'gid://gitlab/List/2',
+ originalIssue = { foo: 'bar' },
+ originalIndex = 0,
+ moveBeforeId = undefined,
+ moveAfterId = undefined,
+ } = {}) => {
+ state = {
+ boardLists: {
+ [fromListId]: { listType: fromListType },
+ [toListId]: { listType: toListType },
+ },
+ boardItems: { [itemId]: originalIssue },
+ boardItemsByListId: { [fromListId]: [123], [toListId]: [] },
+ };
+ params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId };
+ moveMutations = [
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: toListId, moveBeforeId, moveAfterId },
+ },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: fromListId, atIndex: originalIndex },
+ },
+ ];
+ undoMutations = [
+ { type: types.UPDATE_BOARD_ITEM, payload: originalIssue },
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: toListId } },
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: { itemId, listId: fromListId, atIndex: originalIndex },
+ },
+ ];
+ },
+ );
+
+ it('moveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.moveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: moveMutations,
+ });
+ });
+
+ it('undoMoveIssueCard commits a correct set of actions', () => {
+ testAction({
+ action: actions.undoMoveIssueCard,
+ state,
+ payload: getMoveData(state, params),
+ expectedMutations: undoMutations,
+ });
+ });
+ });
});
+});
+
+describe('updateMovedIssueCard', () => {
+ const label1 = {
+ id: 'label1',
+ };
+
+ it.each([
+ [
+ 'issue without a label is moved to a label list',
+ {
+ state: {
+ boardLists: {
+ from: {},
+ to: {
+ listType: ListType.label,
+ label: label1,
+ },
+ },
+ boardItems: {
+ 1: {
+ labels: [],
+ },
+ },
+ },
+ moveData: {
+ itemId: 1,
+ fromListId: 'from',
+ toListId: 'to',
+ },
+ updatedIssue: { labels: [label1] },
+ },
+ ],
+ ])(
+ 'should commit UPDATE_BOARD_ITEM with a correctly updated issue data when %s',
+ (_, { state, moveData, updatedIssue }) => {
+ testAction({
+ action: actions.updateMovedIssue,
+ payload: moveData,
+ state,
+ expectedMutations: [{ type: types.UPDATE_BOARD_ITEM, payload: updatedIssue }],
+ });
+ },
+ );
+});
+
+describe('updateIssueOrder', () => {
+ const issues = {
+ 436: mockIssue,
+ 437: mockIssue2,
+ };
+
+ const state = {
+ boardItems: issues,
+ boardId: 'gid://gitlab/Board/1',
+ };
+
+ const moveData = {
+ itemId: 436,
+ fromListId: 'gid://gitlab/List/1',
+ toListId: 'gid://gitlab/List/2',
+ };
it('calls mutate with the correct variables', () => {
const mutationVariables = {
@@ -728,61 +977,56 @@ describe('moveIssue', () => {
},
});
- actions.moveIssue(
- { state, commit: () => {} },
- {
- itemId: mockIssue.id,
- itemIid: mockIssue.iid,
- itemPath: mockIssue.referencePath,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- },
- );
+ actions.updateIssueOrder({ state, commit: () => {}, dispatch: () => {} }, { moveData });
expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables);
});
- it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_FAILURE mutation when unsuccessful', (done) => {
+ it('should commit MUTATE_ISSUE_SUCCESS mutation when successful', () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
issueMoveList: {
- issue: {},
- errors: [{ foo: 'bar' }],
+ issue: rawIssue,
+ errors: [],
},
},
});
testAction(
- actions.moveIssue,
- {
- itemId: '436',
- itemIid: mockIssue.iid,
- itemPath: mockIssue.referencePath,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- },
+ actions.updateIssueOrder,
+ { moveData },
state,
[
{
- type: types.MOVE_ISSUE,
- payload: {
- originalIssue: mockIssue,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- },
+ type: types.MUTATE_ISSUE_SUCCESS,
+ payload: { issue: rawIssue },
},
+ ],
+ [],
+ );
+ });
+
+ it('should commit SET_ERROR and dispatch undoMoveIssueCard', () => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: {
+ issueMoveList: {
+ issue: {},
+ errors: [{ foo: 'bar' }],
+ },
+ },
+ });
+
+ testAction(
+ actions.updateIssueOrder,
+ { moveData },
+ state,
+ [
{
- type: types.MOVE_ISSUE_FAILURE,
- payload: {
- originalIssue: mockIssue,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- originalIndex: 0,
- },
+ type: types.SET_ERROR,
+ payload: 'An error occurred while moving the issue. Please try again.',
},
],
- [],
- done,
+ [{ type: 'undoMoveIssueCard', payload: moveData }],
);
});
});
@@ -798,11 +1042,11 @@ describe('setAssignees', () => {
testAction(
actions.setAssignees,
[node],
- { activeIssue: { iid, referencePath: refPath }, commit: () => {} },
+ { activeBoardItem: { iid, referencePath: refPath }, commit: () => {} },
[
{
- type: 'UPDATE_ISSUE_BY_ID',
- payload: { prop: 'assignees', issueId: undefined, value: [node] },
+ type: 'UPDATE_BOARD_ITEM_BY_ID',
+ payload: { prop: 'assignees', itemId: undefined, value: [node] },
},
],
[],
@@ -812,7 +1056,43 @@ describe('setAssignees', () => {
});
});
-describe('createNewIssue', () => {
+describe('addListItem', () => {
+ it('should commit ADD_BOARD_ITEM_TO_LIST and UPDATE_BOARD_ITEM mutations', () => {
+ const payload = {
+ list: mockLists[0],
+ item: mockIssue,
+ position: 0,
+ };
+
+ testAction(actions.addListItem, payload, {}, [
+ {
+ type: types.ADD_BOARD_ITEM_TO_LIST,
+ payload: {
+ listId: mockLists[0].id,
+ itemId: mockIssue.id,
+ atIndex: 0,
+ },
+ },
+ { type: types.UPDATE_BOARD_ITEM, payload: mockIssue },
+ ]);
+ });
+});
+
+describe('removeListItem', () => {
+ it('should commit REMOVE_BOARD_ITEM_FROM_LIST and REMOVE_BOARD_ITEM mutations', () => {
+ const payload = {
+ listId: mockLists[0].id,
+ itemId: mockIssue.id,
+ };
+
+ testAction(actions.removeListItem, payload, {}, [
+ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload },
+ { type: types.REMOVE_BOARD_ITEM, payload: mockIssue.id },
+ ]);
+ });
+});
+
+describe('addListNewIssue', () => {
const state = {
boardType: 'group',
fullPath: 'gitlab-org/gitlab',
@@ -839,19 +1119,7 @@ describe('createNewIssue', () => {
},
};
- it('should return issue from API on success', async () => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: {
- createIssue: {
- issue: mockIssue,
- errors: [],
- },
- },
- });
-
- const result = await actions.createNewIssue({ state }, mockIssue);
- expect(result).toEqual(mockIssue);
- });
+ const fakeList = { id: 'gid://gitlab/List/123' };
it('should add board scope to the issue being created', async () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
@@ -863,7 +1131,11 @@ describe('createNewIssue', () => {
},
});
- await actions.createNewIssue({ state: stateWithBoardConfig }, mockIssue);
+ await actions.addListNewIssue(
+ { dispatch: jest.fn(), commit: jest.fn(), state: stateWithBoardConfig },
+ { issueInput: mockIssue, list: fakeList },
+ );
+
expect(gqlClient.mutate).toHaveBeenCalledWith({
mutation: issueCreateMutation,
variables: {
@@ -890,7 +1162,11 @@ describe('createNewIssue', () => {
const payload = formatIssueInput(issue, stateWithBoardConfig.boardConfig);
- await actions.createNewIssue({ state: stateWithBoardConfig }, issue);
+ await actions.addListNewIssue(
+ { dispatch: jest.fn(), commit: jest.fn(), state: stateWithBoardConfig },
+ { issueInput: issue, list: fakeList },
+ );
+
expect(gqlClient.mutate).toHaveBeenCalledWith({
mutation: issueCreateMutation,
variables: {
@@ -901,51 +1177,92 @@ describe('createNewIssue', () => {
expect(payload.assigneeIds).toEqual(['gid://gitlab/User/1', 'gid://gitlab/User/2']);
});
- it('should commit CREATE_ISSUE_FAILURE mutation when API returns an error', (done) => {
- jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
- data: {
- createIssue: {
- issue: mockIssue,
- errors: [{ foo: 'bar' }],
+ describe('when issue creation mutation request succeeds', () => {
+ it('dispatches a correct set of mutations', () => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: {
+ createIssue: {
+ issue: mockIssue,
+ errors: [],
+ },
},
- },
+ });
+
+ testAction({
+ action: actions.addListNewIssue,
+ payload: {
+ issueInput: mockIssue,
+ list: fakeList,
+ placeholderId: 'tmp',
+ },
+ state,
+ expectedActions: [
+ {
+ type: 'addListItem',
+ payload: {
+ list: fakeList,
+ item: formatIssue({ ...mockIssue, id: 'tmp' }),
+ position: 0,
+ },
+ },
+ { type: 'removeListItem', payload: { listId: fakeList.id, itemId: 'tmp' } },
+ {
+ type: 'addListItem',
+ payload: {
+ list: fakeList,
+ item: formatIssue({ ...mockIssue, id: getIdFromGraphQLId(mockIssue.id) }),
+ position: 0,
+ },
+ },
+ ],
+ });
});
-
- const payload = mockIssue;
-
- testAction(
- actions.createNewIssue,
- payload,
- state,
- [{ type: types.CREATE_ISSUE_FAILURE }],
- [],
- done,
- );
});
-});
-
-describe('addListIssue', () => {
- it('should commit ADD_ISSUE_TO_LIST mutation', (done) => {
- const payload = {
- list: mockLists[0],
- issue: mockIssue,
- position: 0,
- };
- testAction(
- actions.addListIssue,
- payload,
- {},
- [{ type: types.ADD_ISSUE_TO_LIST, payload }],
- [],
- done,
- );
+ describe('when issue creation mutation request fails', () => {
+ it('dispatches a correct set of mutations', () => {
+ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
+ data: {
+ createIssue: {
+ issue: mockIssue,
+ errors: [{ foo: 'bar' }],
+ },
+ },
+ });
+
+ testAction({
+ action: actions.addListNewIssue,
+ payload: {
+ issueInput: mockIssue,
+ list: fakeList,
+ placeholderId: 'tmp',
+ },
+ state,
+ expectedActions: [
+ {
+ type: 'addListItem',
+ payload: {
+ list: fakeList,
+ item: formatIssue({ ...mockIssue, id: 'tmp' }),
+ position: 0,
+ },
+ },
+ { type: 'removeListItem', payload: { listId: fakeList.id, itemId: 'tmp' } },
+ ],
+ expectedMutations: [
+ {
+ type: types.SET_ERROR,
+ payload: 'An error occurred while creating the issue. Please try again.',
+ },
+ ],
+ });
+ });
});
});
describe('setActiveIssueLabels', () => {
const state = { boardItems: { [mockIssue.id]: mockIssue } };
- const getters = { activeIssue: mockIssue };
+ const getters = { activeBoardItem: mockIssue };
const testLabelIds = labels.map((label) => label.id);
const input = {
addLabelIds: testLabelIds,
@@ -959,7 +1276,7 @@ describe('setActiveIssueLabels', () => {
.mockResolvedValue({ data: { updateIssue: { issue: { labels: { nodes: labels } } } } });
const payload = {
- issueId: getters.activeIssue.id,
+ itemId: getters.activeBoardItem.id,
prop: 'labels',
value: labels,
};
@@ -970,7 +1287,7 @@ describe('setActiveIssueLabels', () => {
{ ...state, ...getters },
[
{
- type: types.UPDATE_ISSUE_BY_ID,
+ type: types.UPDATE_BOARD_ITEM_BY_ID,
payload,
},
],
@@ -990,7 +1307,7 @@ describe('setActiveIssueLabels', () => {
describe('setActiveIssueDueDate', () => {
const state = { boardItems: { [mockIssue.id]: mockIssue } };
- const getters = { activeIssue: mockIssue };
+ const getters = { activeBoardItem: mockIssue };
const testDueDate = '2020-02-20';
const input = {
dueDate: testDueDate,
@@ -1010,7 +1327,7 @@ describe('setActiveIssueDueDate', () => {
});
const payload = {
- issueId: getters.activeIssue.id,
+ itemId: getters.activeBoardItem.id,
prop: 'dueDate',
value: testDueDate,
};
@@ -1021,7 +1338,7 @@ describe('setActiveIssueDueDate', () => {
{ ...state, ...getters },
[
{
- type: types.UPDATE_ISSUE_BY_ID,
+ type: types.UPDATE_BOARD_ITEM_BY_ID,
payload,
},
],
@@ -1039,9 +1356,15 @@ describe('setActiveIssueDueDate', () => {
});
});
-describe('setActiveIssueSubscribed', () => {
- const state = { boardItems: { [mockActiveIssue.id]: mockActiveIssue } };
- const getters = { activeIssue: mockActiveIssue };
+describe('setActiveItemSubscribed', () => {
+ const state = {
+ boardItems: {
+ [mockActiveIssue.id]: mockActiveIssue,
+ },
+ fullPath: 'gitlab-org',
+ issuableType: 'issue',
+ };
+ const getters = { activeBoardItem: mockActiveIssue, isEpicBoard: false };
const subscribedState = true;
const input = {
subscribedState,
@@ -1051,7 +1374,7 @@ describe('setActiveIssueSubscribed', () => {
it('should commit subscribed status', (done) => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
- issueSetSubscription: {
+ updateIssuableSubscription: {
issue: {
subscribed: subscribedState,
},
@@ -1061,18 +1384,18 @@ describe('setActiveIssueSubscribed', () => {
});
const payload = {
- issueId: getters.activeIssue.id,
+ itemId: getters.activeBoardItem.id,
prop: 'subscribed',
value: subscribedState,
};
testAction(
- actions.setActiveIssueSubscribed,
+ actions.setActiveItemSubscribed,
input,
{ ...state, ...getters },
[
{
- type: types.UPDATE_ISSUE_BY_ID,
+ type: types.UPDATE_BOARD_ITEM_BY_ID,
payload,
},
],
@@ -1084,15 +1407,15 @@ describe('setActiveIssueSubscribed', () => {
it('throws error if fails', async () => {
jest
.spyOn(gqlClient, 'mutate')
- .mockResolvedValue({ data: { issueSetSubscription: { errors: ['failed mutation'] } } });
+ .mockResolvedValue({ data: { updateIssuableSubscription: { errors: ['failed mutation'] } } });
- await expect(actions.setActiveIssueSubscribed({ getters }, input)).rejects.toThrow(Error);
+ await expect(actions.setActiveItemSubscribed({ getters }, input)).rejects.toThrow(Error);
});
});
describe('setActiveIssueMilestone', () => {
const state = { boardItems: { [mockIssue.id]: mockIssue } };
- const getters = { activeIssue: mockIssue };
+ const getters = { activeBoardItem: mockIssue };
const testMilestone = {
...mockMilestone,
id: 'gid://gitlab/Milestone/1',
@@ -1115,7 +1438,7 @@ describe('setActiveIssueMilestone', () => {
});
const payload = {
- issueId: getters.activeIssue.id,
+ itemId: getters.activeBoardItem.id,
prop: 'milestone',
value: testMilestone,
};
@@ -1126,7 +1449,7 @@ describe('setActiveIssueMilestone', () => {
{ ...state, ...getters },
[
{
- type: types.UPDATE_ISSUE_BY_ID,
+ type: types.UPDATE_BOARD_ITEM_BY_ID,
payload,
},
],
@@ -1144,9 +1467,13 @@ describe('setActiveIssueMilestone', () => {
});
});
-describe('setActiveIssueTitle', () => {
- const state = { boardItems: { [mockIssue.id]: mockIssue } };
- const getters = { activeIssue: mockIssue };
+describe('setActiveItemTitle', () => {
+ const state = {
+ boardItems: { [mockIssue.id]: mockIssue },
+ issuableType: 'issue',
+ fullPath: 'path/f',
+ };
+ const getters = { activeBoardItem: mockIssue, isEpicBoard: false };
const testTitle = 'Test Title';
const input = {
title: testTitle,
@@ -1156,7 +1483,7 @@ describe('setActiveIssueTitle', () => {
it('should commit title after setting the issue', (done) => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
- updateIssue: {
+ updateIssuableTitle: {
issue: {
title: testTitle,
},
@@ -1166,18 +1493,18 @@ describe('setActiveIssueTitle', () => {
});
const payload = {
- issueId: getters.activeIssue.id,
+ itemId: getters.activeBoardItem.id,
prop: 'title',
value: testTitle,
};
testAction(
- actions.setActiveIssueTitle,
+ actions.setActiveItemTitle,
input,
{ ...state, ...getters },
[
{
- type: types.UPDATE_ISSUE_BY_ID,
+ type: types.UPDATE_BOARD_ITEM_BY_ID,
payload,
},
],
@@ -1191,7 +1518,7 @@ describe('setActiveIssueTitle', () => {
.spyOn(gqlClient, 'mutate')
.mockResolvedValue({ data: { updateIssue: { errors: ['failed mutation'] } } });
- await expect(actions.setActiveIssueTitle({ getters }, input)).rejects.toThrow(Error);
+ await expect(actions.setActiveItemTitle({ getters }, input)).rejects.toThrow(Error);
});
});
@@ -1321,7 +1648,7 @@ describe('toggleBoardItemMultiSelection', () => {
testAction(
actions.toggleBoardItemMultiSelection,
boardItem2,
- { activeId: mockActiveIssue.id, activeIssue: mockActiveIssue, selectedBoardItems: [] },
+ { activeId: mockActiveIssue.id, activeBoardItem: mockActiveIssue, selectedBoardItems: [] },
[
{
type: types.ADD_BOARD_ITEM_TO_SELECTION,
@@ -1378,6 +1705,51 @@ describe('toggleBoardItem', () => {
});
});
+describe('setError', () => {
+ it('should commit mutation SET_ERROR', () => {
+ testAction({
+ action: actions.setError,
+ payload: { message: 'mayday' },
+ expectedMutations: [
+ {
+ payload: 'mayday',
+ type: types.SET_ERROR,
+ },
+ ],
+ });
+ });
+
+ it('should capture error using Sentry when captureError is true', () => {
+ jest.spyOn(Sentry, 'captureException');
+
+ const mockError = new Error();
+ actions.setError(
+ { commit: () => {} },
+ {
+ message: 'mayday',
+ error: mockError,
+ captureError: true,
+ },
+ );
+
+ expect(Sentry.captureException).toHaveBeenNthCalledWith(1, mockError);
+ });
+});
+
+describe('unsetError', () => {
+ it('should commit mutation SET_ERROR with undefined as payload', () => {
+ testAction({
+ action: actions.unsetError,
+ expectedMutations: [
+ {
+ payload: undefined,
+ type: types.SET_ERROR,
+ },
+ ],
+ });
+ });
+});
+
describe('fetchBacklog', () => {
expectNotImplemented(actions.fetchBacklog);
});
diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js
index 32d73d861bc..6114ba0af5f 100644
--- a/spec/frontend/boards/stores/getters_spec.js
+++ b/spec/frontend/boards/stores/getters_spec.js
@@ -88,7 +88,7 @@ describe('Boards - Getters', () => {
});
});
- describe('activeIssue', () => {
+ describe('activeBoardItem', () => {
it.each`
id | expected
${'1'} | ${'issue'}
@@ -96,7 +96,7 @@ describe('Boards - Getters', () => {
`('returns $expected when $id is passed to state', ({ id, expected }) => {
const state = { boardItems: { 1: 'issue' }, activeId: id };
- expect(getters.activeIssue(state)).toEqual(expected);
+ expect(getters.activeBoardItem(state)).toEqual(expected);
});
});
@@ -105,14 +105,14 @@ describe('Boards - Getters', () => {
const mockActiveIssue = {
referencePath: 'gitlab-org/gitlab-test#1',
};
- expect(getters.groupPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual(
+ expect(getters.groupPathForActiveIssue({}, { activeBoardItem: mockActiveIssue })).toEqual(
'gitlab-org',
);
});
it('returns empty string as group path when active issue is an empty object', () => {
const mockActiveIssue = {};
- expect(getters.groupPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual('');
+ expect(getters.groupPathForActiveIssue({}, { activeBoardItem: mockActiveIssue })).toEqual('');
});
});
@@ -121,14 +121,16 @@ describe('Boards - Getters', () => {
const mockActiveIssue = {
referencePath: 'gitlab-org/gitlab-test#1',
};
- expect(getters.projectPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual(
+ expect(getters.projectPathForActiveIssue({}, { activeBoardItem: mockActiveIssue })).toEqual(
'gitlab-org/gitlab-test',
);
});
it('returns empty string as project path when active issue is an empty object', () => {
const mockActiveIssue = {};
- expect(getters.projectPathForActiveIssue({}, { activeIssue: mockActiveIssue })).toEqual('');
+ expect(getters.projectPathForActiveIssue({}, { activeBoardItem: mockActiveIssue })).toEqual(
+ '',
+ );
});
});
@@ -177,4 +179,31 @@ describe('Boards - Getters', () => {
expect(getters.activeGroupProjects(state)).toEqual([mockGroupProject1]);
});
});
+
+ describe('isIssueBoard', () => {
+ it.each`
+ issuableType | expected
+ ${'issue'} | ${true}
+ ${'epic'} | ${false}
+ `(
+ 'returns $expected when issuableType on state is $issuableType',
+ ({ issuableType, expected }) => {
+ const state = {
+ issuableType,
+ };
+
+ expect(getters.isIssueBoard(state)).toBe(expected);
+ },
+ );
+ });
+
+ describe('isEpicBoard', () => {
+ afterEach(() => {
+ window.gon = { features: {} };
+ });
+
+ it('returns false', () => {
+ expect(getters.isEpicBoard()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index 33897cc0250..af6d439e294 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -1,3 +1,4 @@
+import { cloneDeep } from 'lodash';
import { issuableTypes } from '~/boards/constants';
import * as types from '~/boards/stores/mutation_types';
import mutations from '~/boards/stores/mutations';
@@ -9,6 +10,7 @@ import {
mockIssue2,
mockGroupProjects,
labels,
+ mockList,
} from '../mock_data';
const expectNotImplemented = (action) => {
@@ -25,6 +27,14 @@ describe('Board Store Mutations', () => {
'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();
});
@@ -335,7 +345,7 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.REQUEST_ADD_ISSUE);
});
- describe('UPDATE_ISSUE_BY_ID', () => {
+ describe('UPDATE_BOARD_ITEM_BY_ID', () => {
const issueId = '1';
const prop = 'id';
const value = '2';
@@ -353,8 +363,8 @@ describe('Board Store Mutations', () => {
describe('when the issue is in state', () => {
it('updates the property of the correct issue', () => {
- mutations.UPDATE_ISSUE_BY_ID(state, {
- issueId,
+ mutations.UPDATE_BOARD_ITEM_BY_ID(state, {
+ itemId: issueId,
prop,
value,
});
@@ -366,8 +376,8 @@ describe('Board Store Mutations', () => {
describe('when the issue is not in state', () => {
it('throws an error', () => {
expect(() => {
- mutations.UPDATE_ISSUE_BY_ID(state, {
- issueId: '3',
+ mutations.UPDATE_BOARD_ITEM_BY_ID(state, {
+ itemId: '3',
prop,
value,
});
@@ -384,41 +394,7 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_ERROR);
});
- describe('MOVE_ISSUE', () => {
- it('updates boardItemsByListId, moving issue between lists', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
- 'gid://gitlab/List/2': [],
- };
-
- const issues = {
- 1: mockIssue,
- 2: mockIssue2,
- };
-
- state = {
- ...state,
- boardItemsByListId: listIssues,
- boardLists: initialBoardListsState,
- boardItems: issues,
- };
-
- mutations.MOVE_ISSUE(state, {
- originalIssue: mockIssue2,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- });
-
- const updatedListIssues = {
- 'gid://gitlab/List/1': [mockIssue.id],
- 'gid://gitlab/List/2': [mockIssue2.id],
- };
-
- expect(state.boardItemsByListId).toEqual(updatedListIssues);
- });
- });
-
- describe('MOVE_ISSUE_SUCCESS', () => {
+ describe('MUTATE_ISSUE_SUCCESS', () => {
it('updates issue in issues state', () => {
const issues = {
436: { id: rawIssue.id },
@@ -429,7 +405,7 @@ describe('Board Store Mutations', () => {
boardItems: issues,
};
- mutations.MOVE_ISSUE_SUCCESS(state, {
+ mutations.MUTATE_ISSUE_SUCCESS(state, {
issue: rawIssue,
});
@@ -437,33 +413,24 @@ describe('Board Store Mutations', () => {
});
});
- describe('MOVE_ISSUE_FAILURE', () => {
- it('updates boardItemsByListId, reverting moving issue between lists, and sets error message', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [mockIssue.id],
- 'gid://gitlab/List/2': [mockIssue2.id],
- };
+ 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' } } };
- state = {
- ...state,
- boardItemsByListId: listIssues,
- boardLists: initialBoardListsState,
- };
+ mutations.UPDATE_BOARD_ITEM(state, updatedIssue);
- mutations.MOVE_ISSUE_FAILURE(state, {
- originalIssue: mockIssue2,
- fromListId: 'gid://gitlab/List/1',
- toListId: 'gid://gitlab/List/2',
- originalIndex: 1,
- });
+ expect(state.boardItems.some_gid).toEqual(updatedIssue);
+ });
+ });
- const updatedListIssues = {
- 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
- 'gid://gitlab/List/2': [],
- };
+ 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.boardItemsByListId).toEqual(updatedListIssues);
- expect(state.error).toEqual('An error occurred while moving the issue. Please try again.');
+ expect(state.boardItems).toEqual({ some_gid2: {} });
});
});
@@ -479,85 +446,89 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.RECEIVE_UPDATE_ISSUE_ERROR);
});
- describe('CREATE_ISSUE_FAILURE', () => {
- it('sets error message on state', () => {
- mutations.CREATE_ISSUE_FAILURE(state);
+ 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],
+ },
+ ],
+ ])(`inserts an item into a list %s`, (_, { payload, listState }) => {
+ mutations.ADD_BOARD_ITEM_TO_LIST(state, payload);
- expect(state.error).toBe('An error occurred while creating the issue. Please try again.');
+ expect(state.boardItemsByListId[payload.listId]).toEqual(listState);
});
- });
-
- describe('ADD_ISSUE_TO_LIST', () => {
- it('adds issue to issues state and issue id in list in boardItemsByListId', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [mockIssue.id],
- };
- const issues = {
- 1: mockIssue,
- };
-
- state = {
- ...state,
- boardItemsByListId: listIssues,
- boardItems: issues,
- boardLists: initialBoardListsState,
- };
+ it("updates the list's items count", () => {
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
- mutations.ADD_ISSUE_TO_LIST(state, { list: mockLists[0], issue: mockIssue2 });
+ mutations.ADD_BOARD_ITEM_TO_LIST(state, {
+ itemId: mockIssue2.id,
+ listId: mockList.id,
+ });
- expect(state.boardItemsByListId['gid://gitlab/List/1']).toContain(mockIssue2.id);
- expect(state.boardItems[mockIssue2.id]).toEqual(mockIssue2);
expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(2);
});
});
- describe('ADD_ISSUE_TO_LIST_FAILURE', () => {
- it('removes issue id from list in boardItemsByListId and sets error message', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
- };
- const issues = {
- 1: mockIssue,
- 2: mockIssue2,
- };
-
- state = {
- ...state,
- boardItemsByListId: listIssues,
- boardItems: issues,
- boardLists: initialBoardListsState,
- };
-
- mutations.ADD_ISSUE_TO_LIST_FAILURE(state, { list: mockLists[0], issueId: mockIssue2.id });
-
- expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id);
- expect(state.error).toBe('An error occurred while creating the issue. Please try again.');
+ describe('REMOVE_BOARD_ITEM_FROM_LIST', () => {
+ beforeEach(() => {
+ setBoardsListsState();
});
- });
- describe('REMOVE_ISSUE_FROM_LIST', () => {
- it('removes issue id from list in boardItemsByListId and deletes issue from state', () => {
- const listIssues = {
- 'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
- };
- const issues = {
- 1: mockIssue,
- 2: mockIssue2,
- };
-
- state = {
- ...state,
- boardItemsByListId: listIssues,
- boardItems: issues,
- boardLists: initialBoardListsState,
- };
+ it("removes an item from a list and updates the list's items count", () => {
+ expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
+ expect(state.boardItemsByListId['gid://gitlab/List/1']).toContain(mockIssue.id);
- mutations.ADD_ISSUE_TO_LIST_FAILURE(state, { list: mockLists[0], issueId: mockIssue2.id });
+ mutations.REMOVE_BOARD_ITEM_FROM_LIST(state, {
+ itemId: mockIssue.id,
+ listId: mockList.id,
+ });
- expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue2.id);
- expect(state.boardItems).not.toContain(mockIssue2);
+ expect(state.boardItemsByListId['gid://gitlab/List/1']).not.toContain(mockIssue.id);
+ expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(0);
});
});
@@ -666,4 +637,14 @@ describe('Board Store Mutations', () => {
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');
+ });
+ });
});