Commit d15cc293 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch...

Merge branch '325778-moving-issue-to-board-list-of-different-type-should-not-remove-it-from-previous-list' into 'master'

Clone issue card on move when necessary [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!58644
parents 1d76f120 96b703f5
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,134 @@ export default { ...@@ -333,42 +335,134 @@ 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('updateIssueOrder', { moveData });
},
moveIssueCard: ({ commit }, moveData) => {
const {
reordering,
shouldClone,
itemNotInToList,
originalIndex,
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) {
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,
itemId,
fromListId,
toListId,
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 });
},
updateIssueOrder: 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('updateIssueOrder', {
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: 'updateIssueOrder',
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,
};
This diff is collapsed.
...@@ -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