Commit 237c57b5 authored by Florie Guibert's avatar Florie Guibert

Swimlanes - Update fetching and loading issues strategy

Display swimlanes loading skeleton
Fetch issues per list instead of fetching them per epic to reduce number
of requests
parent 86a6df07
<script>
import { GlSkeletonLoader } from '@gitlab/ui';
export default {
name: 'BoardCardLoading',
components: {
GlSkeletonLoader,
},
};
</script>
<template>
<div
class="gl-mb-3 gl-bg-white gl-rounded-base gl-p-5"
style="border: 1px solid #dfdfdf; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); height: 110px"
>
<div style="width: 340px; height: 100px">
<gl-skeleton-loader :width="340" :height="100">
<rect width="340" height="16" rx="4" />
<rect y="30" width="118" height="16" rx="8" />
<rect x="122" y="30" width="130" height="16" rx="8" />
<rect y="62" width="38" height="16" rx="4" />
<circle cx="320" cy="68" r="16" />
</gl-skeleton-loader>
</div>
</div>
</template>
......@@ -127,7 +127,7 @@ export default {
</component>
<epics-swimlanes
v-else
v-else-if="boardListsToUse.length"
ref="swimlanes"
:lists="boardListsToUse"
:can-admin-list="canAdminList"
......
<script>
import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { GlButton, GlIcon, GlLink, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import createFlash from '~/flash';
import { formatDate } from '~/lib/utils/datetime_utility';
......@@ -13,7 +13,6 @@ export default {
GlButton,
GlIcon,
GlLink,
GlLoadingIcon,
GlPopover,
IssuesLaneList,
},
......@@ -50,7 +49,7 @@ export default {
};
},
computed: {
...mapState(['epicsFlags', 'filterParams']),
...mapState(['filterParams']),
...mapGetters(['getIssuesByEpic']),
isOpen() {
return this.epic.state === statusType.open;
......@@ -82,28 +81,12 @@ export default {
epicDateString() {
return formatDate(this.epic.createdAt);
},
isLoading() {
return Boolean(this.epicsFlags[this.epic.id]?.isLoading);
},
shouldDisplay() {
return this.issuesCount > 0 || this.isLoading;
},
},
watch: {
filterParams: {
handler() {
if (!this.filterParams.epicId || this.filterParams.epicId === this.epic.id) {
this.fetchIssuesForEpic(this.epic.id);
}
},
deep: true,
return this.issuesCount > 0;
},
},
mounted() {
this.fetchIssuesForEpic(this.epic.id);
},
methods: {
...mapActions(['fetchIssuesForEpic', 'updateBoardEpicUserPreferences']),
...mapActions(['updateBoardEpicUserPreferences']),
toggleCollapsed() {
this.isCollapsed = !this.isCollapsed;
......@@ -149,7 +132,6 @@ export default {
<gl-link :href="epic.webUrl" class="gl-font-sm">{{ __('Go to epic') }}</gl-link>
</gl-popover>
<span
v-if="!isLoading"
v-gl-tooltip.hover
:title="issuesCountTooltipText"
class="gl-display-flex gl-align-items-center gl-text-gray-500"
......@@ -160,7 +142,6 @@ export default {
<gl-icon class="gl-mr-2 gl-flex-shrink-0" name="issues" />
<span aria-hidden="true">{{ issuesCount }}</span>
</span>
<gl-loading-icon v-if="isLoading" class="gl-p-2" />
</div>
</div>
<div v-if="!isCollapsed" class="gl-display-flex gl-pb-5" data-testid="board-epic-lane-issues">
......
......@@ -13,6 +13,7 @@ import { calculateSwimlanesBufferSize } from '../boards_util';
import { DRAGGABLE_TAG, EPIC_LANE_BASE_HEIGHT } from '../constants';
import EpicLane from './epic_lane.vue';
import IssuesLaneList from './issues_lane_list.vue';
import SwimlanesLoadingSkeleton from './swimlanes_loading_skeleton.vue';
export default {
EpicLane,
......@@ -24,6 +25,7 @@ export default {
IssuesLaneList,
GlButton,
GlIcon,
SwimlanesLoadingSkeleton,
VirtualList,
},
directives: {
......@@ -51,7 +53,14 @@ export default {
};
},
computed: {
...mapState(['epics', 'pageInfoByListId', 'listsFlags', 'addColumnForm']),
...mapState([
'epics',
'pageInfoByListId',
'listsFlags',
'addColumnForm',
'filterParams',
'epicsSwimlanesFetchInProgress',
]),
...mapGetters(['getUnassignedIssues']),
addColumnFormVisible() {
return this.addColumnForm?.visible;
......@@ -89,12 +98,34 @@ export default {
this.lists.some((list) => this.pageInfoByListId[list.id]?.hasNextPage)
);
},
isLoading() {
const {
epicLanesFetchInProgress,
listItemsFetchInProgress,
} = this.epicsSwimlanesFetchInProgress;
return epicLanesFetchInProgress && listItemsFetchInProgress;
},
},
watch: {
filterParams: {
handler() {
Promise.all(
this.lists.map((list) => {
return this.fetchItemsForList({ listId: list.id });
}),
)
.then(() => this.doneLoadingSwimlanesItems())
.catch(() => {});
},
deep: true,
immediate: true,
},
},
mounted() {
this.bufferSize = calculateSwimlanesBufferSize(this.$el.offsetTop);
},
methods: {
...mapActions(['moveList', 'fetchItemsForList']),
...mapActions(['moveList', 'fetchItemsForList', 'doneLoadingSwimlanesItems']),
handleDragOnEnd(params) {
const { newIndex, oldIndex, item, to } = params;
const { listId } = item.dataset;
......@@ -146,7 +177,8 @@ export default {
data-testid="board-swimlanes"
data_qa_selector="board_epics_swimlanes"
>
<div>
<swimlanes-loading-skeleton v-if="isLoading" />
<div v-else>
<component
:is="treeRootWrapper"
v-bind="treeRootOptions"
......
<script>
import BoardCardLoadingSkeleton from '~/boards/components/board_card_loading_skeleton.vue';
export default {
components: {
BoardCardLoadingSkeleton,
},
};
</script>
<template>
<div class="gl-px-3">
<div class="gl-mt-6 gl-bg-gray-100 gl-display-inline-flex gl-rounded-base">
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3">
<board-card-loading-skeleton />
</div>
</div>
<br />
<div class="gl-mt-6 gl-bg-gray-100 gl-display-inline-flex gl-rounded-base">
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3">
<board-card-loading-skeleton />
</div>
</div>
<br />
<div class="gl-mt-6 gl-bg-gray-100 gl-display-inline-flex gl-rounded-base">
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3 gl-mr-3">
<board-card-loading-skeleton />
</div>
<div class="gl-px-3 gl-pt-3">
<board-card-loading-skeleton />
</div>
</div>
</div>
</template>
......@@ -346,22 +346,6 @@ export default {
.catch(() => commit(types.RECEIVE_ITEMS_FOR_LIST_FAILURE, listId));
},
fetchIssuesForEpic: ({ state, commit }, epicId) => {
commit(types.REQUEST_ISSUES_FOR_EPIC, epicId);
const { filterParams } = state;
const variables = {
filters: { ...filterParams, epicId },
};
return fetchAndFormatListIssues(state, variables)
.then(({ listItems }) => {
commit(types.RECEIVE_ISSUES_FOR_EPIC_SUCCESS, { ...listItems, epicId });
})
.catch(() => commit(types.RECEIVE_ISSUES_FOR_EPIC_FAILURE, epicId));
},
toggleEpicSwimlanes: ({ state, commit, dispatch }) => {
commit(types.TOGGLE_EPICS_SWIMLANES);
......@@ -386,6 +370,10 @@ export default {
commit(types.SET_EPICS_SWIMLANES);
},
doneLoadingSwimlanesItems: ({ commit }) => {
commit(types.DONE_LOADING_SWIMLANES_ITEMS);
},
resetEpics: ({ commit }) => {
commit(types.RESET_EPICS);
},
......
......@@ -11,11 +11,9 @@ export const TOGGLE_PROMOTION_STATE = 'TOGGLE_PROMOTION_STATE';
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
export const RECEIVE_ITEMS_FOR_LIST_FAILURE = 'RECEIVE_ITEMS_FOR_LIST_FAILURE';
export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS';
export const REQUEST_ISSUES_FOR_EPIC = 'REQUEST_ISSUES_FOR_EPIC';
export const RECEIVE_ISSUES_FOR_EPIC_SUCCESS = 'RECEIVE_ISSUES_FOR_EPIC_SUCCESS';
export const RECEIVE_ISSUES_FOR_EPIC_FAILURE = 'RECEIVE_ISSUES_FOR_EPIC_FAILURE';
export const TOGGLE_EPICS_SWIMLANES = 'TOGGLE_EPICS_SWIMLANES';
export const SET_EPICS_SWIMLANES = 'SET_EPICS_SWIMLANES';
export const DONE_LOADING_SWIMLANES_ITEMS = 'DONE_LOADING_SWIMLANES_ITEMS';
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
export const RECEIVE_BOARD_LISTS_FAILURE = 'RECEIVE_BOARD_LISTS_FAILURE';
export const UPDATE_LIST_SUCCESS = 'UPDATE_LIST_SUCCESS';
......
......@@ -77,11 +77,11 @@ export default {
union(state.boardItemsByListId[listId] || [], listData[listId]),
);
Vue.set(state.pageInfoByListId, listId, listPageInfo[listId]);
Vue.set(state.listsFlags, listId, {
isLoading: false,
isLoadingMore: false,
unassignedIssuesCount: noEpicIssues ? listItemsCount : undefined,
});
Vue.set(state.listsFlags[listId], 'isLoading', false);
Vue.set(state.listsFlags[listId], 'isLoadingMore', false);
if (noEpicIssues) {
Vue.set(state.listsFlags[listId], 'unassignedIssuesCount', listItemsCount);
}
},
[mutationTypes.RECEIVE_ITEMS_FOR_LIST_FAILURE]: (state, listId) => {
......@@ -92,48 +92,41 @@ export default {
Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false });
},
[mutationTypes.REQUEST_ISSUES_FOR_EPIC]: (state, epicId) => {
Vue.set(state.epicsFlags, epicId, { isLoading: true });
},
[mutationTypes.RECEIVE_ISSUES_FOR_EPIC_SUCCESS]: (state, { listData, boardItems, epicId }) => {
Object.entries(listData).forEach(([listId, list]) => {
Vue.set(
state.boardItemsByListId,
listId,
union(state.boardItemsByListId[listId] || [], list),
);
});
Vue.set(state, 'boardItems', { ...state.boardItems, ...boardItems });
Vue.set(state.epicsFlags, epicId, { isLoading: false });
},
[mutationTypes.RECEIVE_ISSUES_FOR_EPIC_FAILURE]: (state, epicId) => {
state.error = s__('Boards|An error occurred while fetching issues. Please reload the page.');
Vue.set(state.epicsFlags, epicId, { isLoading: false });
},
[mutationTypes.TOGGLE_EPICS_SWIMLANES]: (state) => {
state.isShowingEpicsSwimlanes = !state.isShowingEpicsSwimlanes;
state.epicsSwimlanesFetchInProgress = true;
Vue.set(state, 'epicsSwimlanesFetchInProgress', {
epicLanesFetchInProgress: true,
listItemsFetchInProgress: true,
});
},
[mutationTypes.SET_EPICS_SWIMLANES]: (state) => {
state.isShowingEpicsSwimlanes = true;
state.epicsSwimlanesFetchInProgress = true;
Vue.set(state, 'epicsSwimlanesFetchInProgress', {
epicLanesFetchInProgress: true,
listItemsFetchInProgress: true,
});
},
[mutationTypes.DONE_LOADING_SWIMLANES_ITEMS]: (state) => {
Vue.set(state, 'epicsSwimlanesFetchInProgress', {
...state.epicsSwimlanesFetchInProgress,
listItemsFetchInProgress: false,
});
},
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, boardLists) => {
state.boardLists = boardLists;
state.epicsSwimlanesFetchInProgress = false;
},
[mutationTypes.RECEIVE_SWIMLANES_FAILURE]: (state) => {
state.error = s__(
'Boards|An error occurred while fetching the board swimlanes. Please reload the page.',
);
state.epicsSwimlanesFetchInProgress = false;
Vue.set(state, 'epicsSwimlanesFetchInProgress', {
...state.epicsSwimlanesFetchInProgress,
epicLanesFetchInProgress: false,
});
},
[mutationTypes.RECEIVE_EPICS_SUCCESS]: (state, { epics, canAdminEpic }) => {
......
......@@ -5,13 +5,15 @@ export default () => ({
canAdminEpic: false,
isShowingEpicsSwimlanes: false,
epicsSwimlanesFetchInProgress: false,
epicsSwimlanesFetchInProgress: {
epicLanesFetchInProgress: false,
listItemsFetchInProgress: false,
},
// The epic data stored in 'epics' do not always persist
// and will be cleared with changes to the filter.
epics: [],
epicsCacheById: {},
epicFetchInProgress: false,
epicsFlags: {},
milestones: [],
milestonesLoading: false,
iterations: [],
......
---
title: Add Swimlanes loading skeleton
merge_request: 57661
author:
type: changed
......@@ -32,6 +32,8 @@ RSpec.describe 'epics swimlanes', :js do
it 'displays epics swimlanes when link to boards with group_by epic in URL' do
expect(page).to have_selector('[data-testid="board-swimlanes"]')
wait_for_all_requests
epic_lanes = page.all(:css, '.board-epic-lane')
expect(epic_lanes.length).to eq(2)
end
......
import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { GlIcon } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import EpicLane from 'ee/boards/components/epic_lane.vue';
......@@ -16,29 +16,21 @@ describe('EpicLane', () => {
const updateBoardEpicUserPreferencesSpy = jest.fn();
const createStore = ({ isLoading = false, boardItemsByListId = mockIssuesByListId }) => {
const createStore = ({ boardItemsByListId = mockIssuesByListId }) => {
return new Vuex.Store({
actions: {
fetchIssuesForEpic: jest.fn(),
updateBoardEpicUserPreferences: updateBoardEpicUserPreferencesSpy,
},
state: {
boardItemsByListId,
boardItems: issues,
epicsFlags: {
[mockEpic.id]: { isLoading },
},
},
getters,
});
};
const createComponent = ({
props = {},
isLoading = false,
boardItemsByListId = mockIssuesByListId,
} = {}) => {
const store = createStore({ isLoading, boardItemsByListId });
const createComponent = ({ props = {}, boardItemsByListId = mockIssuesByListId } = {}) => {
const store = createStore({ boardItemsByListId });
const defaultProps = {
epic: mockEpic,
......@@ -93,16 +85,6 @@ describe('EpicLane', () => {
});
});
it('does not display loading icon when issues are not loading', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
});
it('displays loading icon and hides issues count when issues are loading', () => {
createComponent({ isLoading: true });
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(findByTestId('epic-lane-issue-count').exists()).toBe(false);
});
it('invokes `updateBoardEpicUserPreferences` method on collapse', () => {
const collapsedValue = false;
......
......@@ -8,6 +8,7 @@ import { calculateSwimlanesBufferSize } from 'ee/boards/boards_util';
import EpicLane from 'ee/boards/components/epic_lane.vue';
import EpicsSwimlanes from 'ee/boards/components/epics_swimlanes.vue';
import IssueLaneList from 'ee/boards/components/issues_lane_list.vue';
import SwimlanesLoadingSkeleton from 'ee/boards/components/swimlanes_loading_skeleton.vue';
import { EPIC_LANE_BASE_HEIGHT } from 'ee/boards/constants';
import getters from 'ee/boards/stores/getters';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
......@@ -19,7 +20,12 @@ jest.mock('ee/boards/boards_util');
describe('EpicsSwimlanes', () => {
let wrapper;
const createStore = () => {
const fetchItemsForListSpy = jest.fn();
const createStore = ({
epicLanesFetchInProgress = false,
listItemsFetchInProgress = false,
} = {}) => {
return new Vuex.Store({
state: {
epics: mockEpics,
......@@ -37,13 +43,25 @@ describe('EpicsSwimlanes', () => {
unassignedIssuesCount: 1,
},
},
epicsSwimlanesFetchInProgress: {
epicLanesFetchInProgress,
listItemsFetchInProgress,
},
},
getters,
actions: {
fetchItemsForList: fetchItemsForListSpy,
},
});
};
const createComponent = ({ canAdminList = false, swimlanesBufferedRendering = false } = {}) => {
const store = createStore();
const createComponent = ({
canAdminList = false,
swimlanesBufferedRendering = false,
epicLanesFetchInProgress = false,
listItemsFetchInProgress = false,
} = {}) => {
const store = createStore({ epicLanesFetchInProgress, listItemsFetchInProgress });
const defaultProps = {
lists: mockLists,
disabled: false,
......@@ -62,6 +80,11 @@ describe('EpicsSwimlanes', () => {
wrapper.destroy();
});
it('calls fetchItemsForList on mounted', () => {
createComponent();
expect(fetchItemsForListSpy).toHaveBeenCalled();
});
describe('computed', () => {
describe('treeRootWrapper', () => {
describe('when canAdminList prop is true', () => {
......@@ -121,6 +144,23 @@ describe('EpicsSwimlanes', () => {
});
});
describe('Loading skeleton', () => {
it.each`
epicLanesFetchInProgress | listItemsFetchInProgress | expected
${true} | ${true} | ${true}
${false} | ${true} | ${false}
${true} | ${false} | ${false}
${false} | ${false} | ${false}
`(
'loading is $expected when epicLanesFetchInProgress is $epicLanesFetchInProgress and listItemsFetchInProgress is $listItemsFetchInProgress',
({ epicLanesFetchInProgress, listItemsFetchInProgress, expected }) => {
createComponent({ epicLanesFetchInProgress, listItemsFetchInProgress });
expect(wrapper.find(SwimlanesLoadingSkeleton).exists()).toBe(expected);
},
);
});
describe('when swimlanesBufferedRendering is true', () => {
const bufferSize = 100;
......
......@@ -9,7 +9,7 @@ import * as types from 'ee/boards/stores/mutation_types';
import mutations from 'ee/boards/stores/mutations';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { formatListIssues, formatBoardLists } from '~/boards/boards_util';
import { formatBoardLists } from '~/boards/boards_util';
import { issuableTypes } from '~/boards/constants';
import * as typesCE from '~/boards/stores/mutation_types';
import * as commonUtils from '~/lib/utils/common_utils';
......@@ -480,71 +480,6 @@ describe('updateIssueWeight', () => {
expectNotImplemented(actions.updateIssueWeight);
});
describe('fetchIssuesForEpic', () => {
const listId = mockLists[0].id;
const epicId = mockEpic.id;
const state = {
fullPath: 'gitlab-org',
boardId: 1,
filterParams: {},
boardType: 'group',
};
const queryResponse = {
data: {
group: {
board: {
lists: {
nodes: [
{
id: listId,
issues: {
edges: [{ node: [mockIssue] }],
},
},
],
},
},
},
},
};
const formattedIssues = formatListIssues(queryResponse.data.group.board.lists);
it('should commit mutations REQUEST_ISSUES_FOR_EPIC and RECEIVE_ITEMS_FOR_LIST_SUCCESS on success', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchIssuesForEpic,
epicId,
state,
[
{ type: types.REQUEST_ISSUES_FOR_EPIC, payload: epicId },
{ type: types.RECEIVE_ISSUES_FOR_EPIC_SUCCESS, payload: { ...formattedIssues, epicId } },
],
[],
done,
);
});
it('should commit mutations REQUEST_ISSUES_FOR_EPIC and RECEIVE_ITEMS_FOR_LIST_FAILURE on failure', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
testAction(
actions.fetchIssuesForEpic,
epicId,
state,
[
{ type: types.REQUEST_ISSUES_FOR_EPIC, payload: epicId },
{ type: types.RECEIVE_ISSUES_FOR_EPIC_FAILURE, payload: epicId },
],
[],
done,
);
});
});
describe('toggleEpicSwimlanes', () => {
it('should commit mutation TOGGLE_EPICS_SWIMLANES', () => {
const startURl = `${TEST_HOST}/groups/gitlab-org/-/boards/1?group_by=epic`;
......@@ -608,8 +543,6 @@ describe('toggleEpicSwimlanes', () => {
describe('setEpicSwimlanes', () => {
it('should commit mutation SET_EPICS_SWIMLANES', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue({});
return testAction(
actions.setEpicSwimlanes,
null,
......@@ -620,6 +553,18 @@ describe('setEpicSwimlanes', () => {
});
});
describe('doneLoadingSwimlanesItems', () => {
it('should commit mutation DONE_LOADING_SWIMLANES_ITEMS', () => {
return testAction(
actions.doneLoadingSwimlanesItems,
null,
{},
[{ type: types.DONE_LOADING_SWIMLANES_ITEMS }],
[],
);
});
});
describe('resetEpics', () => {
it('commits RESET_EPICS mutation', () => {
return testAction(actions.resetEpics, {}, {}, [{ type: types.RESET_EPICS }], []);
......
......@@ -18,9 +18,6 @@ let state = {
boardItemsByListId: {},
boardItems: {},
boardLists: initialBoardListsState,
epicsFlags: {
[epicId]: { isLoading: true },
},
};
describe('SET_SHOW_LABELS', () => {
......@@ -76,53 +73,6 @@ describe('TOGGLE_PROMOTION_STATE', () => {
expectNotImplemented(mutations.TOGGLE_PROMOTION_STATE);
});
describe('REQUEST_ISSUES_FOR_EPIC', () => {
it('sets isLoading epicsFlags in state for epicId to true', () => {
state = {
...state,
epicsFlags: {
[epicId]: { isLoading: false },
},
};
mutations.REQUEST_ISSUES_FOR_EPIC(state, epicId);
expect(state.epicsFlags[epicId].isLoading).toBe(true);
});
});
describe('RECEIVE_ISSUES_FOR_EPIC_SUCCESS', () => {
it('sets boardItemsByListId and issues state for epic issues and loading state to false', () => {
const listIssues = {
'gid://gitlab/List/1': [mockIssue.id],
'gid://gitlab/List/2': [mockIssue2.id],
};
const issues = {
436: mockIssue,
437: mockIssue2,
};
mutations.RECEIVE_ISSUES_FOR_EPIC_SUCCESS(state, {
listData: listIssues,
boardItems: issues,
epicId,
});
expect(state.boardItemsByListId).toEqual(listIssues);
expect(state.boardItems).toEqual(issues);
expect(state.epicsFlags[epicId].isLoading).toBe(false);
});
});
describe('RECEIVE_ISSUES_FOR_EPIC_FAILURE', () => {
it('sets loading state to false for epic and error message', () => {
mutations.RECEIVE_ISSUES_FOR_EPIC_FAILURE(state, epicId);
expect(state.error).toEqual('An error occurred while fetching issues. Please reload the page.');
expect(state.epicsFlags[epicId].isLoading).toBe(false);
});
});
describe('TOGGLE_EPICS_SWIMLANES', () => {
it('toggles isShowingEpicsSwimlanes from true to false', () => {
state = {
......@@ -149,12 +99,18 @@ describe('TOGGLE_EPICS_SWIMLANES', () => {
it('sets epicsSwimlanesFetchInProgress to true', () => {
state = {
...state,
epicsSwimlanesFetchInProgress: false,
epicsSwimlanesFetchInProgress: {
epicLanesFetchInProgress: false,
listItemsFetchInProgress: false,
},
};
mutations.TOGGLE_EPICS_SWIMLANES(state);
expect(state.epicsSwimlanesFetchInProgress).toBe(true);
expect(state.epicsSwimlanesFetchInProgress).toEqual({
epicLanesFetchInProgress: true,
listItemsFetchInProgress: true,
});
});
});
......@@ -163,42 +119,63 @@ describe('SET_EPICS_SWIMLANES', () => {
state = {
...state,
isShowingEpicsSwimlanes: false,
epicsSwimlanesFetchInProgress: false,
epicsSwimlanesFetchInProgress: {
epicLanesFetchInProgress: false,
listItemsFetchInProgress: false,
},
};
mutations.SET_EPICS_SWIMLANES(state);
expect(state.isShowingEpicsSwimlanes).toBe(true);
expect(state.epicsSwimlanesFetchInProgress).toBe(true);
expect(state.epicsSwimlanesFetchInProgress).toEqual({
epicLanesFetchInProgress: true,
listItemsFetchInProgress: true,
});
});
});
describe('DONE_LOADING_SWIMLANES_ITEMS', () => {
it('set listItemsFetchInProgress to false', () => {
state = {
...state,
epicsSwimlanesFetchInProgress: {
listItemsFetchInProgress: true,
},
};
mutations.DONE_LOADING_SWIMLANES_ITEMS(state);
expect(state.epicsSwimlanesFetchInProgress.listItemsFetchInProgress).toBe(false);
});
});
describe('RECEIVE_BOARD_LISTS_SUCCESS', () => {
it('sets epicsSwimlanesFetchInProgress to false and populates boardLists with payload', () => {
it('populates boardLists with payload', () => {
state = {
...state,
epicsSwimlanesFetchInProgress: true,
boardLists: {},
};
mutations.RECEIVE_BOARD_LISTS_SUCCESS(state, initialBoardListsState);
expect(state.epicsSwimlanesFetchInProgress).toBe(false);
expect(state.boardLists).toEqual(initialBoardListsState);
});
});
describe('RECEIVE_SWIMLANES_FAILURE', () => {
it('sets epicsSwimlanesFetchInProgress to false and sets error message', () => {
it('sets epicLanesFetchInProgress to false and sets error message', () => {
state = {
...state,
epicsSwimlanesFetchInProgress: true,
epicsSwimlanesFetchInProgress: {
epicLanesFetchInProgress: true,
},
error: undefined,
};
mutations.RECEIVE_SWIMLANES_FAILURE(state);
expect(state.epicsSwimlanesFetchInProgress).toBe(false);
expect(state.epicsSwimlanesFetchInProgress.epicLanesFetchInProgress).toBe(false);
expect(state.error).toEqual(
'An error occurred while fetching the board swimlanes. Please reload the page.',
);
......
......@@ -4943,9 +4943,6 @@ msgstr ""
msgid "Boards|An error occurred while fetching group projects. Please try again."
msgstr ""
msgid "Boards|An error occurred while fetching issues. Please reload the page."
msgstr ""
msgid "Boards|An error occurred while fetching labels. Please reload the page."
msgstr ""
......
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