Commit cb2d374b authored by Florie Guibert's avatar Florie Guibert

Boards refactor - Fetch issues with GraphQL

Fetch issues per list with VueX action
parent 3d9f7a44
<script>
import { mapGetters, mapActions } from 'vuex';
import Sortable from 'sortablejs';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
import EmptyComponent from '~/vue_shared/components/empty_component';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import BoardBlankState from './board_blank_state.vue';
import BoardList from './board_list.vue';
import boardsStore from '../stores/boards_store';
......@@ -21,7 +23,7 @@ export default {
directives: {
Tooltip,
},
mixins: [isWipLimitsOn],
mixins: [isWipLimitsOn, glFeatureFlagMixin()],
props: {
list: {
type: Object,
......@@ -62,6 +64,7 @@ export default {
};
},
computed: {
...mapGetters(['getIssues']),
showBoardListAndBoardInfo() {
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
},
......@@ -69,19 +72,36 @@ export default {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
listIssues() {
if (!this.glFeatures.graphqlBoardLists) {
return this.list.issues;
}
return this.getIssues(this.list.id);
},
shouldFetchIssues() {
return this.glFeatures.graphqlBoardLists && this.list.type !== ListType.blank;
},
},
watch: {
filter: {
handler() {
if (this.shouldFetchIssues) {
this.fetchIssuesForList(this.list.id);
} else {
this.list.page = 1;
this.list.getIssues(true).catch(() => {
// TODO: handle request error
});
}
},
deep: true,
},
},
mounted() {
if (this.shouldFetchIssues) {
this.fetchIssuesForList(this.list.id);
}
const instance = this;
const sortableOptions = getBoardSortableDefaultOptions({
......@@ -108,6 +128,7 @@ export default {
Sortable.create(this.$el.parentNode, sortableOptions);
},
methods: {
...mapActions(['fetchIssuesForList']),
showListNewIssueForm(listId) {
eventHub.$emit('showForm', listId);
},
......@@ -142,7 +163,7 @@ export default {
:disabled="disabled"
:group-id="groupId || null"
:issue-link-base="issueLinkBase"
:issues="list.issues"
:issues="listIssues"
:list="list"
:loading="list.loading"
:root-path="rootPath"
......
......@@ -6,6 +6,7 @@ import boardCard from './board_card.vue';
import eventHub from '../eventhub';
import boardsStore from '../stores/boards_store';
import { sprintf, __ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import {
getBoardSortableDefaultOptions,
......@@ -24,6 +25,7 @@ export default {
boardNewIssue,
GlLoadingIcon,
},
mixins: [glFeatureFlagMixin()],
props: {
groupId: {
type: Number,
......@@ -83,6 +85,7 @@ export default {
deep: true,
},
issues() {
if (this.glFeatures.graphqlBoardLists) return;
this.$nextTick(() => {
if (
this.scrollHeight() <= this.listHeight() &&
......@@ -413,6 +416,8 @@ export default {
this.showIssueForm = !this.showIssueForm;
},
onScroll() {
if (this.glFeatures.graphqlBoardLists) return;
if (!this.list.loadingMore && this.scrollTop() > this.scrollHeight() - this.scrollOffset) {
this.loadNextPage();
}
......
......@@ -129,6 +129,9 @@ export default {
collapsedTooltipTitle() {
return this.listTitle || this.listAssignee;
},
shouldDisplaySwimlanes() {
return this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn;
},
},
methods: {
...mapActions(['updateList']),
......@@ -158,7 +161,7 @@ export default {
}
},
updateListFunction() {
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesHeader) {
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded });
} else {
this.list.update();
......@@ -184,7 +187,7 @@ export default {
<h3
:class="{
'user-can-drag': !disabled && !list.preset,
'gl-py-3': !list.isExpanded && !isSwimlanesHeader,
'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader,
'gl-border-b-0': !list.isExpanded || isSwimlanesHeader,
'gl-py-2': !list.isExpanded && isSwimlanesHeader,
}"
......
......@@ -42,6 +42,9 @@ export default {
}
return this.title === '';
},
shouldDisplaySwimlanes() {
return this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn;
},
},
mounted() {
this.$refs.input.focus();
......@@ -75,7 +78,7 @@ export default {
eventHub.$emit(`scroll-board-list-${this.list.id}`);
this.cancel();
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn) {
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
this.addListIssue({ list: this.list, issue, position: 0 });
}
......@@ -85,7 +88,7 @@ export default {
// Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$refs.submitButton).enable();
if (!this.glFeatures.boardsWithSwimlanes || !this.isSwimlanesOn) {
if (!this.shouldDisplaySwimlanes && !this.glFeatures.graphqlBoardLists) {
boardsStore.setIssueDetail(issue);
boardsStore.setListDetail(this.list);
}
......@@ -95,7 +98,7 @@ export default {
$(this.$refs.submitButton).enable();
// Remove the issue
if (this.glFeatures.boardsWithSwimlanes && this.isSwimlanesOn) {
if (this.shouldDisplaySwimlanes || this.glFeatures.graphqlBoardLists) {
this.addListIssueFailure({ list: this.list, issue });
} else {
this.list.removeIssue(issue);
......
......@@ -27,7 +27,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
updateObject(path) {
this.store.path = path.substr(1);
if (gon.features.boardsWithSwimlanes) {
if (gon.features.boardsWithSwimlanes || gon.features.graphqlBoardLists) {
boardsStore.updateFiltersUrl();
boardsStore.performSearch();
}
......
......@@ -7,15 +7,10 @@ fragment IssueNode on Issue {
referencePath: reference(full: true)
dueDate
timeEstimate
weight
confidential
webUrl
subscribed
blocked
relativePosition
epic {
id
}
assignees {
nodes {
...User
......
#import "./issue.fragment.graphql"
#import "ee_else_ce/boards/queries/issue.fragment.graphql"
query ListIssues(
$fullPath: ID!
$boardId: ID!
$id: ID
$filters: BoardIssueInput
$isGroup: Boolean = false
$isProject: Boolean = false
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
board(id: $boardId) {
lists {
lists(id: $id) {
nodes {
id
issues(filters: $filters) {
......@@ -23,7 +24,7 @@ query ListIssues(
}
project(fullPath: $fullPath) @include(if: $isProject) {
board(id: $boardId) {
lists {
lists(id: $id) {
nodes {
id
issues(filters: $filters) {
......
......@@ -79,10 +79,10 @@ export default {
lists = lists.nodes.map(list =>
boardStore.updateListPosition({
...list,
id: getIdFromGraphQLId(list.id),
doNotFetchIssues: true,
}),
);
commit(types.RECEIVE_LISTS, sortBy(lists, 'position'));
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, sortBy(lists, 'position'));
// Backlog list needs to be created if it doesn't exist
if (!lists.find(l => l.type === ListType.backlog)) {
dispatch('createList', { backlog: true });
......@@ -113,7 +113,7 @@ export default {
commit(types.CREATE_LIST_FAILURE);
} else {
const list = data.boardListCreate?.list;
dispatch('addList', { ...list, id: getIdFromGraphQLId(list.id) });
dispatch('addList', list);
}
})
.catch(() => {
......@@ -124,8 +124,8 @@ export default {
addList: ({ state, commit }, list) => {
const lists = state.boardLists;
// Temporarily using positioning logic from boardStore
lists.push(boardStore.updateListPosition(list));
commit(types.RECEIVE_LISTS, sortBy(lists, 'position'));
lists.push(boardStore.updateListPosition({ ...list, doNotFetchIssues: true }));
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, sortBy(lists, 'position'));
},
showWelcomeList: ({ state, dispatch }) => {
......@@ -197,8 +197,33 @@ export default {
notImplemented();
},
fetchIssuesForList: () => {
notImplemented();
fetchIssuesForList: ({ state, commit }, listId) => {
const { endpoints, boardType, filterParams } = state;
const { fullPath, boardId } = endpoints;
const variables = {
fullPath,
boardId: fullBoardId(boardId),
id: listId,
filters: filterParams,
isGroup: boardType === BoardType.group,
isProject: boardType === BoardType.project,
};
return gqlClient
.query({
query: listsIssuesQuery,
context: {
isSingleRequest: true,
},
variables,
})
.then(({ data }) => {
const { lists } = data[boardType]?.board;
const listIssues = formatListIssues(lists);
commit(types.RECEIVE_ISSUES_FOR_LIST_SUCCESS, { listIssues, listId });
})
.catch(() => commit(types.RECEIVE_ISSUES_FOR_LIST_FAILURE, listId));
},
fetchIssuesForAllLists: ({ state, commit }) => {
......
......@@ -304,7 +304,11 @@ const boardsStore = {
onNewListIssueResponse(list, issue, data) {
issue.refreshData(data);
if (!gon.features.boardsWithSwimlanes && list.issuesSize > 1) {
if (
!gon.features.boardsWithSwimlanes &&
!gon.features.graphqlBoardLists &&
list.issues.length > 1
) {
const moveBeforeId = list.issues[1].id;
this.moveIssue(issue.id, null, null, null, moveBeforeId);
}
......@@ -723,6 +727,10 @@ const boardsStore = {
newListIssue(list, issue) {
list.addIssue(issue, null, 0);
list.issuesSize += 1;
let listId = list.id;
if (typeof listId === 'string') {
listId = getIdFromGraphQLId(listId);
}
return this.newIssue(list.id, issue)
.then(res => res.data)
......
......@@ -14,6 +14,11 @@ export default {
return state.issues[id] || {};
},
getIssues: (state, getters) => listId => {
const listIssueIds = state.issuesByListId[listId] || [];
return listIssueIds.map(id => getters.getIssueById(id));
},
getActiveIssue: state => {
return state.issues[state.activeId] || {};
},
......
......@@ -2,7 +2,7 @@ export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const SET_FILTERS = 'SET_FILTERS';
export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS';
export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE';
export const RECEIVE_LISTS = 'RECEIVE_LISTS';
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST';
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
......@@ -13,6 +13,8 @@ export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST';
export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS';
export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR';
export const REQUEST_ISSUES_FOR_ALL_LISTS = 'REQUEST_ISSUES_FOR_ALL_LISTS';
export const RECEIVE_ISSUES_FOR_LIST_FAILURE = 'RECEIVE_ISSUES_FOR_LIST_FAILURE';
export const RECEIVE_ISSUES_FOR_LIST_SUCCESS = 'RECEIVE_ISSUES_FOR_LIST_SUCCESS';
export const RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS = 'RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS';
export const RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE = 'RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE';
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
......
......@@ -35,7 +35,7 @@ export default {
state.showPromotion = showPromotion;
},
[mutationTypes.RECEIVE_LISTS]: (state, lists) => {
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => {
state.boardLists = lists;
},
......@@ -89,6 +89,20 @@ export default {
notImplemented();
},
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_SUCCESS]: (state, { listIssues, listId }) => {
const { listData, issues } = listIssues;
Vue.set(state, 'issues', { ...state.issues, ...issues });
Vue.set(state.issuesByListId, listId, listData[listId]);
const listIndex = state.boardLists.findIndex(l => l.id === listId);
Vue.set(state.boardLists[listIndex], 'loading', false);
},
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_FAILURE]: (state, listId) => {
state.error = __('An error occurred while fetching the board issues. Please reload the page.');
const listIndex = state.boardLists.findIndex(l => l.id === listId);
Vue.set(state.boardLists[listIndex], 'loading', false);
},
[mutationTypes.REQUEST_ISSUES_FOR_ALL_LISTS]: state => {
state.isLoadingIssues = true;
},
......
......@@ -116,7 +116,6 @@
.board-title {
flex-direction: column;
height: 100%;
}
.board-title-caret {
......
......@@ -19,7 +19,7 @@
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
= render 'shared/issuable/search_bar', type: :boards, board: board
- if Feature.enabled?(:boards_with_swimlanes, current_board_parent)
- if Feature.enabled?(:boards_with_swimlanes, current_board_parent) || Feature.enabled?(:graphql_board_lists, current_board_parent)
%board-content{ "v-cloak" => "true",
"ref" => "board_content",
":lists" => "state.lists",
......
#import "~/graphql_shared/fragments/user.fragment.graphql"
fragment IssueNode on Issue {
id
iid
title
referencePath: reference(full: true)
dueDate
timeEstimate
weight
confidential
webUrl
subscribed
blocked
relativePosition
epic {
id
}
assignees {
nodes {
...User
}
}
labels {
nodes {
id
title
color
description
}
}
}
......@@ -3,11 +3,6 @@ import gettersCE from '~/boards/stores/getters';
export default {
...gettersCE,
getIssues: (state, getters) => listId => {
const listIssueIds = state.issuesByListId[listId] || [];
return listIssueIds.map(id => getters.getIssueById(id));
},
getIssuesByEpic: (state, getters) => (listId, epicId) => {
return getters.getIssues(listId).filter(issue => issue.epic && issue.epic.id === epicId);
},
......
......@@ -15,6 +15,8 @@ RSpec.describe 'label issues', :js do
before do
stub_licensed_features(multiple_group_issue_boards: true)
# stubbing until sidebar work is done: https://gitlab.com/gitlab-org/gitlab/-/issues/230711
stub_feature_flags(graphql_board_lists: false)
group.add_maintainer(user)
sign_in(user)
......
import getters from 'ee/boards/stores/getters';
import {
mockIssue,
mockIssue2,
mockIssue3,
mockIssue4,
mockIssues,
......@@ -15,16 +14,6 @@ describe('EE Boards Store Getters', () => {
issues,
};
describe('getIssues', () => {
it('returns issues for a given listId', () => {
const getIssueById = issueId => [mockIssue, mockIssue2].find(({ id }) => id === issueId);
expect(getters.getIssues(boardsState, { getIssueById })('gid://gitlab/List/2')).toEqual(
mockIssues,
);
});
});
describe('getIssuesByEpic', () => {
it('returns issues for a given listId and epicId', () => {
const getIssues = () => mockIssues;
......
......@@ -19,6 +19,8 @@ RSpec.describe 'Group Issue Boards', :js do
let(:card) { find('.board:nth-child(1)').first('.board-card') }
before do
# stubbing until sidebar work is done: https://gitlab.com/gitlab-org/gitlab/-/issues/230711
stub_feature_flags(graphql_board_lists: false)
sign_in(user)
visit group_board_path(group, board)
......
......@@ -98,12 +98,35 @@ export const mockMilestone = {
due_date: '2019-12-31',
};
const assignees = [
{
id: 'gid://gitlab/User/2',
username: 'angelina.herman',
name: 'Bernardina Bosco',
avatar: 'https://www.gravatar.com/avatar/eb7b664b13a30ad9f9ba4b61d7075470?s=80&d=identicon',
webUrl: 'http://127.0.0.1:3000/angelina.herman',
},
];
const labels = [
{
id: 'gid://gitlab/GroupLabel/5',
title: 'Cosync',
color: '#34ebec',
description: null,
},
];
export const rawIssue = {
title: 'Testing',
id: 'gid://gitlab/Issue/1',
iid: 1,
title: 'Issue 1',
id: 'gid://gitlab/Issue/436',
iid: 27,
dueDate: null,
timeEstimate: 0,
weight: null,
confidential: false,
referencePath: 'gitlab-org/gitlab-test#1',
referencePath: 'gitlab-org/gitlab-test#27',
path: '/gitlab-org/gitlab-test/-/issues/27',
labels: {
nodes: [
{
......@@ -115,23 +138,24 @@ export const rawIssue = {
],
},
assignees: {
nodes: [
{
id: 1,
name: 'name',
username: 'username',
avatar_url: 'http://avatar_url',
nodes: assignees,
},
],
epic: {
id: 'gid://gitlab/Epic/41',
},
};
export const mockIssue = {
title: 'Testing',
id: 1,
iid: 1,
id: 'gid://gitlab/Issue/436',
iid: 27,
title: 'Issue 1',
dueDate: null,
timeEstimate: 0,
weight: null,
confidential: false,
referencePath: 'gitlab-org/gitlab-test#1',
referencePath: 'gitlab-org/gitlab-test#27',
path: '/gitlab-org/gitlab-test/-/issues/27',
assignees,
labels: [
{
id: 1,
......@@ -140,44 +164,64 @@ export const mockIssue = {
description: 'testing',
},
],
assignees: [
{
id: 1,
name: 'name',
username: 'username',
avatar_url: 'http://avatar_url',
epic: {
id: 'gid://gitlab/Epic/41',
},
],
};
export const mockIssueWithModel = new ListIssue(mockIssue);
export const mockIssue2 = {
title: 'Planning',
id: 2,
iid: 2,
id: 'gid://gitlab/Issue/437',
iid: 28,
title: 'Issue 2',
dueDate: null,
timeEstimate: 0,
weight: null,
confidential: false,
referencePath: 'gitlab-org/gitlab-test#2',
labels: [
{
id: 1,
title: 'plan',
color: 'blue',
description: 'planning',
},
],
assignees: [
{
id: 1,
name: 'name',
username: 'username',
avatar_url: 'http://avatar_url',
path: '/gitlab-org/gitlab-test/-/issues/28',
assignees,
labels,
epic: {
id: 'gid://gitlab/Epic/40',
},
],
};
export const mockIssue2WithModel = new ListIssue(mockIssue2);
export const mockIssue3 = {
id: 'gid://gitlab/Issue/438',
iid: 29,
title: 'Issue 3',
referencePath: '#29',
dueDate: null,
timeEstimate: 0,
weight: null,
confidential: false,
path: '/gitlab-org/gitlab-test/-/issues/28',
assignees,
labels,
epic: null,
};
export const mockIssue4 = {
id: 'gid://gitlab/Issue/439',
iid: 30,
title: 'Issue 4',
referencePath: '#30',
dueDate: null,
timeEstimate: 0,
weight: null,
confidential: false,
path: '/gitlab-org/gitlab-test/-/issues/28',
assignees,
labels,
epic: null,
};
export const mockIssues = [mockIssue, mockIssue2];
export const BoardsMockData = {
GET: {
'/test/-/boards/1/lists/300/issues?id=300&page=1': {
......@@ -239,6 +283,7 @@ export const mockLists = [
label: null,
assignee: null,
milestone: null,
loading: false,
},
{
id: 'gid://gitlab/List/2',
......@@ -255,9 +300,22 @@ export const mockLists = [
},
assignee: null,
milestone: null,
loading: false,
},
];
export const mockListsWithModel = mockLists.map(listMock =>
Vue.observable(new List({ ...listMock, doNotFetchIssues: true })),
);
export const mockIssuesByListId = {
'gid://gitlab/List/1': [mockIssue.id, mockIssue3.id, mockIssue4.id],
'gid://gitlab/List/2': mockIssues.map(({ id }) => id),
};
export const issues = {
[mockIssue.id]: mockIssue,
[mockIssue2.id]: mockIssue2,
[mockIssue3.id]: mockIssue3,
[mockIssue4.id]: mockIssue4,
};
......@@ -3,7 +3,6 @@ import {
mockListsWithModel,
mockLists,
mockIssue,
mockIssue2,
mockIssueWithModel,
mockIssue2WithModel,
rawIssue,
......@@ -134,7 +133,7 @@ describe('createList', () => {
{ backlog: true },
state,
[],
[{ type: 'addList', payload: { ...backlogList, id: 1 } }],
[{ type: 'addList', payload: backlogList }],
done,
);
});
......@@ -232,19 +231,15 @@ describe('deleteList', () => {
expectNotImplemented(actions.deleteList);
});
describe('fetchIssuesForList', () => {
expectNotImplemented(actions.fetchIssuesForList);
});
describe('moveIssue', () => {
const listIssues = {
'gid://gitlab/List/1': [mockIssue.id, mockIssue2.id],
'gid://gitlab/List/1': [436, 437],
'gid://gitlab/List/2': [],
};
const issues = {
'1': mockIssueWithModel,
'2': mockIssue2WithModel,
'436': mockIssueWithModel,
'437': mockIssue2WithModel,
};
const state = {
......@@ -269,7 +264,7 @@ describe('moveIssue', () => {
testAction(
actions.moveIssue,
{
issueId: mockIssue.id,
issueId: '436',
issueIid: mockIssue.iid,
issuePath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
......@@ -308,7 +303,7 @@ describe('moveIssue', () => {
testAction(
actions.moveIssue,
{
issueId: mockIssue.id,
issueId: '436',
issueIid: mockIssue.iid,
issuePath: mockIssue.referencePath,
fromListId: 'gid://gitlab/List/1',
......
import getters from '~/boards/stores/getters';
import { inactiveId } from '~/boards/constants';
import { mockIssue, mockIssue2, mockIssues, mockIssuesByListId, issues } from '../mock_data';
describe('Boards - Getters', () => {
describe('getLabelToggleState', () => {
......@@ -115,4 +116,18 @@ describe('Boards - Getters', () => {
expect(getters.getActiveIssue(state)).toEqual(expected);
});
});
describe('getIssues', () => {
const boardsState = {
issuesByListId: mockIssuesByListId,
issues,
};
it('returns issues for a given listId', () => {
const getIssueById = issueId => [mockIssue, mockIssue2].find(({ id }) => id === issueId);
expect(getters.getIssues(boardsState, { getIssueById })('gid://gitlab/List/2')).toEqual(
mockIssues,
);
});
});
});
......@@ -54,11 +54,11 @@ describe('Board Store Mutations', () => {
});
});
describe('RECEIVE_LISTS', () => {
describe('RECEIVE_BOARD_LISTS_SUCCESS', () => {
it('Should set boardLists to state', () => {
const lists = [listObj, listObjDuplicate];
mutations[types.RECEIVE_LISTS](state, lists);
mutations[types.RECEIVE_BOARD_LISTS_SUCCESS](state, lists);
expect(state.boardLists).toEqual(lists);
});
......@@ -145,6 +145,33 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.RECEIVE_REMOVE_LIST_ERROR);
});
describe('RECEIVE_ISSUES_FOR_LIST_SUCCESS', () => {
it('updates issuesByListId and issues on state', () => {
const listIssues = {
'gid://gitlab/List/1': [mockIssue.id],
};
const issues = {
'1': mockIssue,
};
state = {
...state,
isLoadingIssues: true,
issuesByListId: {},
issues: {},
boardLists: mockListsWithModel,
};
mutations.RECEIVE_ISSUES_FOR_LIST_SUCCESS(state, {
listIssues: { listData: listIssues, issues },
listId: 'gid://gitlab/List/1',
});
expect(state.issuesByListId).toEqual(listIssues);
expect(state.issues).toEqual(issues);
});
});
describe('REQUEST_ISSUES_FOR_ALL_LISTS', () => {
it('sets isLoadingIssues to true', () => {
expect(state.isLoadingIssues).toBe(false);
......@@ -155,10 +182,28 @@ describe('Board Store Mutations', () => {
});
});
describe('RECEIVE_ISSUES_FOR_LIST_FAILURE', () => {
it('sets error message', () => {
state = {
...state,
boardLists: mockListsWithModel,
error: undefined,
};
const listId = 'gid://gitlab/List/1';
mutations.RECEIVE_ISSUES_FOR_LIST_FAILURE(state, listId);
expect(state.error).toEqual(
'An error occurred while fetching the board issues. Please reload the page.',
);
});
});
describe('RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS', () => {
it('sets isLoadingIssues to false and updates issuesByListId object', () => {
const listIssues = {
'': [mockIssue.id],
'gid://gitlab/List/1': [mockIssue.id],
};
const issues = {
'1': mockIssue,
......@@ -287,7 +332,7 @@ describe('Board Store Mutations', () => {
describe('MOVE_ISSUE_SUCCESS', () => {
it('updates issue in issues state', () => {
const issues = {
'1': { id: rawIssue.id },
'436': { id: rawIssue.id },
};
state = {
......@@ -299,7 +344,7 @@ describe('Board Store Mutations', () => {
issue: rawIssue,
});
expect(state.issues).toEqual({ '1': { ...mockIssueWithModel, id: 1 } });
expect(state.issues).toEqual({ '436': { ...mockIssueWithModel, id: 436 } });
});
});
......
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