Commit 1e990c8c authored by Eulyeon Ko's avatar Eulyeon Ko

Clone issue card on move when needed

Also resolves duplicate key error by
checking if issue card already exists
in the destination list
parent f028aff4
import { sortBy } from 'lodash'; import { sortBy, cloneDeep } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { ListType, NOT_FILTER } from './constants'; import { ListType, NOT_FILTER } from './constants';
...@@ -113,6 +113,37 @@ export function formatIssueInput(issueInput, boardConfig) { ...@@ -113,6 +113,37 @@ export function formatIssueInput(issueInput, boardConfig) {
}; };
} }
export function shouldCloneCard(fromListType, toListType) {
const involvesClosed = fromListType === ListType.closed || toListType === ListType.closed;
const involvesBacklog = fromListType === ListType.backlog || toListType === ListType.backlog;
if (involvesClosed || involvesBacklog) {
return false;
}
if (fromListType !== toListType) {
return true;
}
return false;
}
export function getMoveData(state, params) {
const { boardItems, boardItemsByListId, boardLists } = state;
const { itemId, fromListId, toListId } = params;
const fromListType = boardLists[fromListId].listType;
const toListType = boardLists[toListId].listType;
return {
reordering: fromListId === toListId,
shouldClone: shouldCloneCard(fromListType, toListType),
itemNotInToList: !boardItemsByListId[toListId].includes(itemId),
originalIssue: cloneDeep(boardItems[itemId]),
originalIndex: boardItemsByListId[fromListId].indexOf(itemId),
...params,
};
}
export function moveItemListHelper(item, fromList, toList) { export function moveItemListHelper(item, fromList, toList) {
const updatedItem = item; const updatedItem = item;
if ( if (
......
...@@ -190,7 +190,7 @@ export default { ...@@ -190,7 +190,7 @@ export default {
} }
this.moveItem({ this.moveItem({
itemId, itemId: Number(itemId),
itemIid, itemIid,
itemPath, itemPath,
fromListId: from.dataset.listId, fromListId: from.dataset.listId,
......
...@@ -2,6 +2,7 @@ import * as Sentry from '@sentry/browser'; ...@@ -2,6 +2,7 @@ import * as Sentry from '@sentry/browser';
import { pick } from 'lodash'; import { pick } from 'lodash';
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql'; import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql'; import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import { import {
BoardType, BoardType,
ListType, ListType,
...@@ -23,13 +24,14 @@ import { ...@@ -23,13 +24,14 @@ import {
formatIssueInput, formatIssueInput,
updateListPosition, updateListPosition,
transformNotFilters, transformNotFilters,
moveItemListHelper,
getMoveData,
} from '../boards_util'; } from '../boards_util';
import boardLabelsQuery from '../graphql/board_labels.query.graphql'; import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql'; import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql';
import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql'; import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
import groupProjectsQuery from '../graphql/group_projects.query.graphql'; import groupProjectsQuery from '../graphql/group_projects.query.graphql';
import issueCreateMutation from '../graphql/issue_create.mutation.graphql'; import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql'; import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql'; import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql'; import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
...@@ -333,42 +335,123 @@ export default { ...@@ -333,42 +335,123 @@ export default {
dispatch('moveIssue', payload); dispatch('moveIssue', payload);
}, },
moveIssue: ( moveIssue: ({ dispatch, state }, params) => {
{ state, commit }, const moveData = getMoveData(state, params);
{ itemId, itemIid, itemPath, fromListId, toListId, moveBeforeId, moveAfterId },
dispatch('moveIssueCard', moveData);
dispatch('updateMovedIssue', moveData);
dispatch('requestIssueMoveListMutation', { moveData });
},
moveIssueCard: ({ commit }, moveData) => {
const { reordering, shouldClone, itemNotInToList } = moveData;
const { itemId, fromListId, toListId, moveBeforeId, moveAfterId } = moveData;
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId });
if (reordering) {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
itemId,
listId: toListId,
moveBeforeId,
moveAfterId,
});
return;
}
if (itemNotInToList) {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
itemId,
listId: toListId,
moveBeforeId,
moveAfterId,
});
}
if (shouldClone) {
const { originalIndex } = moveData;
commit(types.ADD_BOARD_ITEM_TO_LIST, { itemId, listId: fromListId, atIndex: originalIndex });
}
},
updateMovedIssue: (
{ commit, state: { boardItems, boardLists } },
{ itemId, fromListId, toListId },
) => { ) => {
const originalIssue = state.boardItems[itemId]; const updatedIssue = moveItemListHelper(
const fromList = state.boardItemsByListId[fromListId]; boardItems[itemId],
const originalIndex = fromList.indexOf(Number(itemId)); boardLists[fromListId],
commit(types.MOVE_ISSUE, { originalIssue, fromListId, toListId, moveBeforeId, moveAfterId }); boardLists[toListId],
);
const { boardId } = state; commit(types.UPDATE_BOARD_ITEM, updatedIssue);
const [fullProjectPath] = itemPath.split(/[#]/); },
gqlClient undoMoveIssueCard: ({ commit }, moveData) => {
.mutate({ const { reordering, shouldClone, itemNotInToList } = moveData;
const { itemId, fromListId, toListId } = moveData;
const { originalIssue, originalIndex } = moveData;
commit(types.UPDATE_BOARD_ITEM, originalIssue);
if (reordering) {
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId });
commit(types.ADD_BOARD_ITEM_TO_LIST, { itemId, listId: fromListId, atIndex: originalIndex });
return;
}
if (shouldClone) {
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId });
}
if (itemNotInToList) {
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: toListId });
}
commit(types.ADD_BOARD_ITEM_TO_LIST, { itemId, listId: fromListId, atIndex: originalIndex });
},
requestIssueMoveListMutation: async (
{ commit, dispatch, state },
{ moveData, mutationVariables = {} },
) => {
try {
const { itemId, fromListId, toListId, moveBeforeId, moveAfterId } = moveData;
const {
boardId,
boardItems: {
[itemId]: { iid, referencePath },
},
} = state;
const { data } = await gqlClient.mutate({
mutation: issueMoveListMutation, mutation: issueMoveListMutation,
variables: { variables: {
projectPath: fullProjectPath, iid,
projectPath: referencePath.split(/[#]/)[0],
boardId: fullBoardId(boardId), boardId: fullBoardId(boardId),
iid: itemIid,
fromListId: getIdFromGraphQLId(fromListId), fromListId: getIdFromGraphQLId(fromListId),
toListId: getIdFromGraphQLId(toListId), toListId: getIdFromGraphQLId(toListId),
moveBeforeId, moveBeforeId,
moveAfterId, moveAfterId,
// 'mutationVariables' allows EE code to pass in extra parameters.
...mutationVariables,
}, },
}) });
.then(({ data }) => {
if (data?.issueMoveList?.errors.length) { if (data?.issueMoveList?.errors.length || !data.issueMoveList) {
throw new Error(); throw new Error('issueMoveList empty');
} else {
const issue = data.issueMoveList?.issue;
commit(types.MOVE_ISSUE_SUCCESS, { issue });
} }
})
.catch(() => commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issueMoveList.issue });
commit(types.MOVE_ISSUE_FAILURE, { originalIssue, fromListId, toListId, originalIndex }), } catch {
commit(
types.SET_ERROR,
s__('Boards|An error occurred while moving the issue. Please try again.'),
); );
dispatch('undoMoveIssueCard', moveData);
}
}, },
setAssignees: ({ commit, getters }, assigneeUsernames) => { setAssignees: ({ commit, getters }, assigneeUsernames) => {
......
...@@ -23,12 +23,10 @@ export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS'; ...@@ -23,12 +23,10 @@ export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS';
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE'; export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS'; export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR'; export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR';
export const MOVE_ISSUE = 'MOVE_ISSUE';
export const MOVE_ISSUE_SUCCESS = 'MOVE_ISSUE_SUCCESS';
export const MOVE_ISSUE_FAILURE = 'MOVE_ISSUE_FAILURE';
export const UPDATE_BOARD_ITEM = 'UPDATE_BOARD_ITEM'; export const UPDATE_BOARD_ITEM = 'UPDATE_BOARD_ITEM';
export const REMOVE_BOARD_ITEM = 'REMOVE_BOARD_ITEM'; export const REMOVE_BOARD_ITEM = 'REMOVE_BOARD_ITEM';
export const REQUEST_UPDATE_ISSUE = 'REQUEST_UPDATE_ISSUE'; export const REQUEST_UPDATE_ISSUE = 'REQUEST_UPDATE_ISSUE';
export const MUTATE_ISSUE_SUCCESS = 'MUTATE_ISSUE_SUCCESS';
export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS'; export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS';
export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR'; export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR';
export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST'; export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST';
......
...@@ -2,7 +2,7 @@ import { pull, union } from 'lodash'; ...@@ -2,7 +2,7 @@ import { pull, union } from 'lodash';
import Vue from 'vue'; import Vue from 'vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { formatIssue, moveItemListHelper } from '../boards_util'; import { formatIssue } from '../boards_util';
import { issuableTypes } from '../constants'; import { issuableTypes } from '../constants';
import * as mutationTypes from './mutation_types'; import * as mutationTypes from './mutation_types';
...@@ -183,40 +183,11 @@ export default { ...@@ -183,40 +183,11 @@ export default {
notImplemented(); notImplemented();
}, },
[mutationTypes.MOVE_ISSUE]: ( [mutationTypes.MUTATE_ISSUE_SUCCESS]: (state, { issue }) => {
state,
{ originalIssue, fromListId, toListId, moveBeforeId, moveAfterId },
) => {
const fromList = state.boardLists[fromListId];
const toList = state.boardLists[toListId];
const issue = moveItemListHelper(originalIssue, fromList, toList);
Vue.set(state.boardItems, issue.id, issue);
removeItemFromList({ state, listId: fromListId, itemId: issue.id });
addItemToList({ state, listId: toListId, itemId: issue.id, moveBeforeId, moveAfterId });
},
[mutationTypes.MOVE_ISSUE_SUCCESS]: (state, { issue }) => {
const issueId = getIdFromGraphQLId(issue.id); const issueId = getIdFromGraphQLId(issue.id);
Vue.set(state.boardItems, issueId, formatIssue({ ...issue, id: issueId })); Vue.set(state.boardItems, issueId, formatIssue({ ...issue, id: issueId }));
}, },
[mutationTypes.MOVE_ISSUE_FAILURE]: (
state,
{ originalIssue, fromListId, toListId, originalIndex },
) => {
state.error = s__('Boards|An error occurred while moving the issue. Please try again.');
Vue.set(state.boardItems, originalIssue.id, originalIssue);
removeItemFromList({ state, listId: toListId, itemId: originalIssue.id });
addItemToList({
state,
listId: fromListId,
itemId: originalIssue.id,
atIndex: originalIndex,
});
},
[mutationTypes.REQUEST_UPDATE_ISSUE]: () => { [mutationTypes.REQUEST_UPDATE_ISSUE]: () => {
notImplemented(); notImplemented();
}, },
......
...@@ -147,7 +147,7 @@ export default { ...@@ -147,7 +147,7 @@ export default {
} }
this.moveIssue({ this.moveIssue({
itemId, itemId: Number(itemId),
itemIid, itemIid,
itemPath, itemPath,
fromListId: from.dataset.listId, fromListId: from.dataset.listId,
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
formatListsPageInfo, formatListsPageInfo,
fullBoardId, fullBoardId,
transformNotFilters, transformNotFilters,
getMoveData,
} from '~/boards/boards_util'; } from '~/boards/boards_util';
import { BoardType } from '~/boards/constants'; import { BoardType } from '~/boards/constants';
import eventHub from '~/boards/eventhub'; import eventHub from '~/boards/eventhub';
...@@ -12,7 +13,6 @@ import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql'; ...@@ -12,7 +13,6 @@ import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
import actionsCE from '~/boards/stores/actions'; import actionsCE from '~/boards/stores/actions';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import * as typesCE from '~/boards/stores/mutation_types'; import * as typesCE from '~/boards/stores/mutation_types';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import createGqClient, { fetchPolicies } from '~/lib/graphql'; import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { import {
...@@ -37,7 +37,6 @@ import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql'; ...@@ -37,7 +37,6 @@ import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import groupBoardAssigneesQuery from '../graphql/group_board_assignees.query.graphql'; import groupBoardAssigneesQuery from '../graphql/group_board_assignees.query.graphql';
import groupBoardIterationsQuery from '../graphql/group_board_iterations.query.graphql'; import groupBoardIterationsQuery from '../graphql/group_board_iterations.query.graphql';
import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql'; import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql';
import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import issueSetEpicMutation from '../graphql/issue_set_epic.mutation.graphql'; import issueSetEpicMutation from '../graphql/issue_set_epic.mutation.graphql';
import issueSetWeightMutation from '../graphql/issue_set_weight.mutation.graphql'; import issueSetWeightMutation from '../graphql/issue_set_weight.mutation.graphql';
import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql'; import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql';
...@@ -482,50 +481,35 @@ export default { ...@@ -482,50 +481,35 @@ export default {
} }
}, },
moveIssue: ( moveIssue: ({ dispatch, state }, params) => {
{ state, commit }, const { itemId, epicId } = params;
{ itemId, itemIid, itemPath, fromListId, toListId, moveBeforeId, moveAfterId, epicId }, const moveData = getMoveData(state, params);
) => {
const originalIssue = state.boardItems[itemId]; dispatch('moveIssueCard', moveData);
const fromList = state.boardItemsByListId[fromListId]; dispatch('updateMovedIssue', moveData);
const originalIndex = fromList.indexOf(Number(itemId)); dispatch('updateEpicForIssue', { itemId, epicId });
commit(types.MOVE_ISSUE, { dispatch('requestIssueMoveListMutation', {
originalIssue, moveData,
fromListId, mutationVariables: { epicId },
toListId,
moveBeforeId,
moveAfterId,
epicId,
}); });
},
const { boardId } = state; updateEpicForIssue: ({ commit, state: { boardItems } }, { itemId, epicId }) => {
const [fullProjectPath] = itemPath.split(/[#]/); const issue = boardItems[itemId];
gqlClient if (epicId === null) {
.mutate({ commit(types.UPDATE_BOARD_ITEM_BY_ID, {
mutation: issueMoveListMutation, itemId: issue.id,
variables: { prop: 'epic',
projectPath: fullProjectPath, value: null,
boardId: fullBoardId(boardId), });
iid: itemIid, } else if (epicId !== undefined) {
fromListId: getIdFromGraphQLId(fromListId), commit(types.UPDATE_BOARD_ITEM_BY_ID, {
toListId: getIdFromGraphQLId(toListId), itemId: issue.id,
moveBeforeId, prop: 'epic',
moveAfterId, value: { id: epicId },
epicId, });
},
})
.then(({ data }) => {
if (data?.issueMoveList?.errors.length) {
throw new Error();
} else {
const issue = data.issueMoveList?.issue;
commit(types.MOVE_ISSUE_SUCCESS, { issue });
} }
})
.catch(() =>
commit(types.MOVE_ISSUE_FAILURE, { originalIssue, fromListId, toListId, originalIndex }),
);
}, },
moveEpic: ({ state, commit }, { itemId, fromListId, toListId, moveBeforeId, moveAfterId }) => { moveEpic: ({ state, commit }, { itemId, fromListId, toListId, moveBeforeId, moveAfterId }) => {
......
...@@ -150,25 +150,6 @@ export default { ...@@ -150,25 +150,6 @@ export default {
Vue.set(state, 'epics', []); Vue.set(state, 'epics', []);
}, },
[mutationTypes.MOVE_ISSUE]: (
state,
{ originalIssue, fromListId, toListId, moveBeforeId, moveAfterId, epicId },
) => {
const fromList = state.boardLists[fromListId];
const toList = state.boardLists[toListId];
const issue = moveItemListHelper(originalIssue, fromList, toList);
if (epicId === null) {
Vue.set(state.boardItems, issue.id, { ...issue, epic: null });
} else if (epicId !== undefined) {
Vue.set(state.boardItems, issue.id, { ...issue, epic: { id: epicId } });
}
removeItemFromList({ state, listId: fromListId, itemId: issue.id });
addItemToList({ state, listId: toListId, itemId: issue.id, moveBeforeId, moveAfterId });
},
[mutationTypes.MOVE_EPIC]: ( [mutationTypes.MOVE_EPIC]: (
state, state,
{ originalEpic, fromListId, toListId, moveBeforeId, moveAfterId }, { originalEpic, fromListId, toListId, moveBeforeId, moveAfterId },
......
---
title: Clone issue card on move when necessary in epic swimlanes
merge_request: 58644
author:
type: fixed
...@@ -9,6 +9,7 @@ import * as types from 'ee/boards/stores/mutation_types'; ...@@ -9,6 +9,7 @@ import * as types from 'ee/boards/stores/mutation_types';
import mutations from 'ee/boards/stores/mutations'; import mutations from 'ee/boards/stores/mutations';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { mockMoveIssueParams, mockMoveData, mockMoveState } from 'jest/boards/mock_data';
import { formatBoardLists, formatListIssues } from '~/boards/boards_util'; import { formatBoardLists, formatListIssues } from '~/boards/boards_util';
import { issuableTypes } from '~/boards/constants'; import { issuableTypes } from '~/boards/constants';
import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql'; import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
...@@ -19,13 +20,10 @@ import { ...@@ -19,13 +20,10 @@ import {
labels, labels,
mockLists, mockLists,
mockIssue, mockIssue,
mockIssue2,
mockIssues, mockIssues,
mockEpic, mockEpic,
rawIssue,
mockMilestones, mockMilestones,
mockAssignees, mockAssignees,
mockEpics,
} from '../mock_data'; } from '../mock_data';
Vue.use(Vuex); Vue.use(Vuex);
...@@ -903,204 +901,89 @@ describe.each` ...@@ -903,204 +901,89 @@ describe.each`
}); });
describe('moveIssue', () => { describe('moveIssue', () => {
const epicId = 'gid://gitlab/Epic/1'; it('should dispatch a correct set of actions with epic id', () => {
const params = mockMoveIssueParams;
const listIssues = { const moveData = {
'gid://gitlab/List/1': [436, 437], ...mockMoveData,
'gid://gitlab/List/2': [], epicId: 'some-epic-id',
}; };
const issues = { testAction({
436: mockIssue, action: actions.moveIssue,
437: mockIssue2,
};
const state = {
fullPath: 'gitlab-org',
boardId: 1,
boardType: 'group',
disabled: false,
boardLists: mockLists,
boardItemsByListId: listIssues,
boardItems: issues,
};
it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_SUCCESS mutation when successful', (done) => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
issueMoveList: {
issue: rawIssue,
errors: [],
},
},
});
testAction(
actions.moveIssue,
{
itemId: '436',
itemIid: mockIssue.iid,
itemPath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
epicId,
},
state,
[
{
type: types.MOVE_ISSUE,
payload: { payload: {
originalIssue: mockIssue, ...params,
fromListId: 'gid://gitlab/List/1', epicId: 'some-epic-id',
toListId: 'gid://gitlab/List/2',
epicId,
},
}, },
state: mockMoveState,
expectedActions: [
{ type: 'moveIssueCard', payload: moveData },
{ type: 'updateMovedIssue', payload: moveData },
{ type: 'updateEpicForIssue', payload: { itemId: params.itemId, epicId: 'some-epic-id' } },
{ {
type: types.MOVE_ISSUE_SUCCESS, type: 'requestIssueMoveListMutation',
payload: { issue: rawIssue },
},
],
[],
done,
);
});
it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_FAILURE mutation when unsuccessful', (done) => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
issueMoveList: {
issue: {},
errors: [{ foo: 'bar' }],
},
},
});
testAction(
actions.moveIssue,
{
itemId: '436',
itemIid: mockIssue.iid,
itemPath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
epicId,
},
state,
[
{
type: types.MOVE_ISSUE,
payload: { payload: {
originalIssue: mockIssue, moveData,
fromListId: 'gid://gitlab/List/1', mutationVariables: {
toListId: 'gid://gitlab/List/2', epicId: 'some-epic-id',
epicId,
},
}, },
{
type: types.MOVE_ISSUE_FAILURE,
payload: {
originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
originalIndex: 0,
}, },
}, },
], ],
[], });
done,
);
}); });
}); });
describe('moveEpic', () => { describe('updateEpicForIssue', () => {
const listEpics = { let commonState;
'gid://gitlab/List/1': [41, 40],
'gid://gitlab/List/2': [],
};
const epics = {
41: mockEpic,
40: mockEpics[1],
};
const state = { beforeEach(() => {
fullPath: 'gitlab-org', commonState = {
boardId: 1, boardItems: {
boardType: 'group', itemId: {
disabled: false, id: 'issueId',
boardLists: mockLists,
boardItemsByListId: listEpics,
boardItems: epics,
issuableType: 'epic',
};
it('should commit MOVE_EPIC mutation mutation when successful', async () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
epicMoveList: {
errors: [],
}, },
}, },
};
}); });
await testAction({ it.each([
action: actions.moveEpic, [
'with epic id',
{
payload: { payload: {
itemId: '41', itemId: 'itemId',
fromListId: 'gid://gitlab/List/1', epicId: 'epicId',
toListId: 'gid://gitlab/List/2',
}, },
state,
expectedMutations: [ expectedMutations: [
{ {
type: types.MOVE_EPIC, type: types.UPDATE_BOARD_ITEM_BY_ID,
payload: { payload: { itemId: 'issueId', prop: 'epic', value: { id: 'epicId' } },
originalEpic: mockEpic,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
}, },
], ],
});
});
it('should commit MOVE_EPIC mutation and MOVE_EPIC_FAILURE mutation when unsuccessful', async () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
epicMoveList: {
errors: [{ foo: 'bar' }],
},
}, },
}); ],
[
await testAction({ 'with null as epic id',
action: actions.moveEpic,
payload: {
itemId: '41',
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
state,
expectedMutations: [
{ {
type: types.MOVE_EPIC,
payload: { payload: {
originalEpic: mockEpic, itemId: 'itemId',
fromListId: 'gid://gitlab/List/1', epicId: null,
toListId: 'gid://gitlab/List/2',
},
}, },
expectedMutations: [
{ {
type: types.MOVE_EPIC_FAILURE, type: types.UPDATE_BOARD_ITEM_BY_ID,
payload: { payload: { itemId: 'issueId', prop: 'epic', value: null },
originalEpic: mockEpic,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
originalIndex: 0,
}, },
],
}, },
], ],
])(`commits UPDATE_BOARD_ITEM_BY_ID mutation %s`, (_, { payload, expectedMutations }) => {
testAction({
action: actions.updateEpicForIssue,
payload,
state: commonState,
expectedMutations,
}); });
}); });
}); });
......
import mutations from 'ee/boards/stores/mutations'; import mutations from 'ee/boards/stores/mutations';
import { mockIssue, mockIssue2, mockEpics, mockEpic, mockLists } from '../mock_data'; import { mockEpics, mockEpic, mockLists } from '../mock_data';
const expectNotImplemented = (action) => { const expectNotImplemented = (action) => {
it('is not implemented', () => { it('is not implemented', () => {
...@@ -7,8 +7,6 @@ const expectNotImplemented = (action) => { ...@@ -7,8 +7,6 @@ const expectNotImplemented = (action) => {
}); });
}; };
const epicId = mockEpic.id;
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockLists[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockLists[1], 'gid://gitlab/List/2': mockLists[1],
...@@ -222,64 +220,6 @@ describe('RESET_EPICS', () => { ...@@ -222,64 +220,6 @@ describe('RESET_EPICS', () => {
}); });
}); });
describe('MOVE_ISSUE', () => {
beforeEach(() => {
const listIssues = {
'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
'gid://gitlab/List/2': [],
};
const issues = {
436: mockIssue,
437: mockIssue2,
};
state = {
...state,
boardItemsByListId: listIssues,
boardItems: issues,
};
});
it('updates boardItemsByListId, moving issue between lists and updating epic id on issue', () => {
expect(state.boardItems['437'].epic.id).toEqual('gid://gitlab/Epic/40');
mutations.MOVE_ISSUE(state, {
originalIssue: mockIssue2,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
epicId,
});
const updatedListIssues = {
'gid://gitlab/List/1': [mockIssue.id],
'gid://gitlab/List/2': [mockIssue2.id],
};
expect(state.boardItemsByListId).toEqual(updatedListIssues);
expect(state.boardItems['437'].epic.id).toEqual(epicId);
});
it('removes epic id from issue when epicId is null', () => {
expect(state.boardItems['437'].epic.id).toEqual('gid://gitlab/Epic/40');
mutations.MOVE_ISSUE(state, {
originalIssue: mockIssue2,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
epicId: null,
});
const updatedListIssues = {
'gid://gitlab/List/1': [mockIssue.id],
'gid://gitlab/List/2': [mockIssue2.id],
};
expect(state.boardItemsByListId).toEqual(updatedListIssues);
expect(state.boardItems['437'].epic).toEqual(null);
});
});
describe('MOVE_EPIC', () => { describe('MOVE_EPIC', () => {
it('updates boardItemsByListId, moving epic between lists', () => { it('updates boardItemsByListId, moving epic between lists', () => {
const listIssues = { const listIssues = {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import { keyBy } from 'lodash'; import { keyBy } from 'lodash';
import Vue from 'vue'; import Vue from 'vue';
import '~/boards/models/list'; import '~/boards/models/list';
import { ListType } from '~/boards/constants';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
export const boardObj = { export const boardObj = {
...@@ -488,3 +489,38 @@ export const mockBlockedIssue2 = { ...@@ -488,3 +489,38 @@ export const mockBlockedIssue2 = {
blockedByCount: 4, blockedByCount: 4,
webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0', webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0',
}; };
export const mockMoveIssueParams = {
itemId: 1,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
moveBeforeId: undefined,
moveAfterId: undefined,
};
export const mockMoveState = {
boardLists: {
'gid://gitlab/List/1': {
listType: ListType.backlog,
},
'gid://gitlab/List/2': {
listType: ListType.closed,
},
},
boardItems: {
[mockMoveIssueParams.itemId]: { foo: 'bar' },
},
boardItemsByListId: {
[mockMoveIssueParams.fromListId]: [mockMoveIssueParams.itemId],
[mockMoveIssueParams.toListId]: [],
},
};
export const mockMoveData = {
reordering: false,
shouldClone: false,
itemNotInToList: true,
originalIndex: 0,
originalIssue: { foo: 'bar' },
...mockMoveIssueParams,
};
import * as Sentry from '@sentry/browser'; 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 testAction from 'helpers/vuex_action_helper';
import { import {
fullBoardId, fullBoardId,
...@@ -6,14 +7,15 @@ import { ...@@ -6,14 +7,15 @@ import {
formatBoardLists, formatBoardLists,
formatIssueInput, formatIssueInput,
formatIssue, formatIssue,
getMoveData,
} from '~/boards/boards_util'; } 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 destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '~/boards/graphql/issue_create.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 actions, { gqlClient } from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types'; import * as types from '~/boards/stores/mutation_types';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { import {
mockLists, mockLists,
mockListsById, mockListsById,
...@@ -25,6 +27,9 @@ import { ...@@ -25,6 +27,9 @@ import {
labels, labels,
mockActiveIssue, mockActiveIssue,
mockGroupProjects, mockGroupProjects,
mockMoveIssueParams,
mockMoveState,
mockMoveData,
} from '../mock_data'; } from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -653,64 +658,302 @@ describe('moveItem', () => { ...@@ -653,64 +658,302 @@ describe('moveItem', () => {
}); });
describe('moveIssue', () => { describe('moveIssue', () => {
const listIssues = { it('should dispatch a correct set of actions', () => {
'gid://gitlab/List/1': [436, 437], testAction({
'gid://gitlab/List/2': [], action: actions.moveIssue,
}; payload: mockMoveIssueParams,
state: mockMoveState,
expectedActions: [
{ type: 'moveIssueCard', payload: mockMoveData },
{ type: 'updateMovedIssue', payload: mockMoveData },
{ type: 'requestIssueMoveListMutation', payload: { moveData: mockMoveData } },
],
});
});
});
const issues = { describe('moveIssueCard and undoMoveIssueCard', () => {
436: mockIssue, describe('card should move without clonning', () => {
437: mockIssue2, 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 },
},
];
},
);
const state = { it('moveIssueCard commits a correct set of actions', () => {
fullPath: 'gitlab-org', testAction({
boardId: '1', action: actions.moveIssueCard,
boardType: 'group', state,
disabled: false, payload: getMoveData(state, params),
boardLists: mockLists, expectedMutations: moveMutations,
boardItemsByListId: listIssues, });
boardItems: issues, });
};
it('should commit MOVE_ISSUE mutation and MOVE_ISSUE_SUCCESS mutation when successful', (done) => { it('undoMoveIssueCard commits a correct set of actions', () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({ testAction({
data: { action: actions.undoMoveIssueCard,
issueMoveList: { state,
issue: rawIssue, payload: getMoveData(state, params),
errors: [], expectedMutations: undoMutations,
});
});
});
describe.each([
[
'issue moves out of backlog',
{
fromListType: ListType.backlog,
toListType: ListType.label,
}, },
],
[
'issue card moves to closed',
{
fromListType: ListType.label,
toListType: ListType.closed,
},
],
[
'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,
});
}); });
testAction( it('undoMoveIssueCard commits a correct set of actions', () => {
actions.moveIssue, 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',
{ {
itemId: '436', fromListType: ListType.label,
itemIid: mockIssue.iid, toListType: ListType.assignee,
itemPath: mockIssue.referencePath, },
fromListId: 'gid://gitlab/List/1', ],
toListId: 'gid://gitlab/List/2', ])('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, 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',
{ {
type: types.MOVE_ISSUE, state: {
payload: { boardLists: {
originalIssue: mockIssue, from: {},
fromListId: 'gid://gitlab/List/1', to: {
toListId: 'gid://gitlab/List/2', listType: ListType.label,
label: label1,
}, },
}, },
{ boardItems: {
type: types.MOVE_ISSUE_SUCCESS, 1: {
payload: { issue: rawIssue }, labels: [],
},
},
},
moveData: {
itemId: 1,
fromListId: 'from',
toListId: 'to',
},
updatedIssue: { labels: [label1] },
}, },
], ],
[], ])(
done, '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('requestIssueMoveListMutation', () => {
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', () => { it('calls mutate with the correct variables', () => {
const mutationVariables = { const mutationVariables = {
...@@ -734,61 +977,59 @@ describe('moveIssue', () => { ...@@ -734,61 +977,59 @@ describe('moveIssue', () => {
}, },
}); });
actions.moveIssue( actions.requestIssueMoveListMutation(
{ state, commit: () => {} }, { state, commit: () => {}, dispatch: () => {} },
{ { moveData },
itemId: mockIssue.id,
itemIid: mockIssue.iid,
itemPath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
); );
expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables); 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({ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: { data: {
issueMoveList: { issueMoveList: {
issue: {}, issue: rawIssue,
errors: [{ foo: 'bar' }], errors: [],
}, },
}, },
}); });
testAction( testAction(
actions.moveIssue, actions.requestIssueMoveListMutation,
{ { moveData },
itemId: '436',
itemIid: mockIssue.iid,
itemPath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
},
state, state,
[ [
{ {
type: types.MOVE_ISSUE, type: types.MUTATE_ISSUE_SUCCESS,
payload: { payload: { issue: rawIssue },
originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
}, },
],
[],
);
});
it('should commit SET_ERROR and dispatch undoMoveIssueCard', () => {
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: {
issueMoveList: {
issue: {},
errors: [{ foo: 'bar' }],
}, },
{
type: types.MOVE_ISSUE_FAILURE,
payload: {
originalIssue: mockIssue,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
originalIndex: 0,
}, },
});
testAction(
actions.requestIssueMoveListMutation,
{ moveData },
state,
[
{
type: types.SET_ERROR,
payload: 'An error occurred while moving the issue. Please try again.',
}, },
], ],
[], [{ type: 'undoMoveIssueCard', payload: moveData }],
done,
); );
}); });
}); });
......
...@@ -394,41 +394,7 @@ describe('Board Store Mutations', () => { ...@@ -394,41 +394,7 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_ERROR); expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_ERROR);
}); });
describe('MOVE_ISSUE', () => { describe('MUTATE_ISSUE_SUCCESS', () => {
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', () => {
it('updates issue in issues state', () => { it('updates issue in issues state', () => {
const issues = { const issues = {
436: { id: rawIssue.id }, 436: { id: rawIssue.id },
...@@ -439,7 +405,7 @@ describe('Board Store Mutations', () => { ...@@ -439,7 +405,7 @@ describe('Board Store Mutations', () => {
boardItems: issues, boardItems: issues,
}; };
mutations.MOVE_ISSUE_SUCCESS(state, { mutations.MUTATE_ISSUE_SUCCESS(state, {
issue: rawIssue, issue: rawIssue,
}); });
...@@ -447,36 +413,6 @@ describe('Board Store Mutations', () => { ...@@ -447,36 +413,6 @@ 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],
};
state = {
...state,
boardItemsByListId: listIssues,
boardLists: initialBoardListsState,
};
mutations.MOVE_ISSUE_FAILURE(state, {
originalIssue: mockIssue2,
fromListId: 'gid://gitlab/List/1',
toListId: 'gid://gitlab/List/2',
originalIndex: 1,
});
const updatedListIssues = {
'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
'gid://gitlab/List/2': [],
};
expect(state.boardItemsByListId).toEqual(updatedListIssues);
expect(state.error).toEqual('An error occurred while moving the issue. Please try again.');
});
});
describe('UPDATE_BOARD_ITEM', () => { describe('UPDATE_BOARD_ITEM', () => {
it('updates the given issue in state.boardItems', () => { it('updates the given issue in state.boardItems', () => {
const updatedIssue = { id: 'some_gid', foo: 'bar' }; const updatedIssue = { id: 'some_gid', foo: 'bar' };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment