Commit 91ef152f authored by charlie ablett's avatar charlie ablett

Merge branch '233438-epic-board-display-epics-in-lists' into 'master'

Epic Board - Fetch and display empty lists [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!54169
parents 320e69a7 5a19b0b5
......@@ -46,7 +46,7 @@ export default {
watch: {
filterParams: {
handler() {
this.fetchIssuesForList({ listId: this.list.id });
this.fetchItemsForList({ listId: this.list.id });
},
deep: true,
immediate: true,
......@@ -63,7 +63,7 @@ export default {
},
},
methods: {
...mapActions(['fetchIssuesForList']),
...mapActions(['fetchItemsForList']),
},
};
</script>
......
......@@ -11,7 +11,10 @@ import BoardColumnDeprecated from './board_column_deprecated.vue';
export default {
components: {
BoardColumn: gon.features?.graphqlBoardLists ? BoardColumn : BoardColumnDeprecated,
BoardColumn:
gon.features?.graphqlBoardLists || gon.features?.epicBoards
? BoardColumn
: BoardColumnDeprecated,
BoardContentSidebar: () => import('ee_component/boards/components/board_content_sidebar.vue'),
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
GlAlert,
......@@ -33,10 +36,10 @@ export default {
},
},
computed: {
...mapState(['boardLists', 'error']),
...mapState(['boardLists', 'error', 'isEpicBoard']),
...mapGetters(['isSwimlanesOn']),
boardListsToUse() {
return this.glFeatures.graphqlBoardLists || this.isSwimlanesOn
return this.glFeatures.graphqlBoardLists || this.isSwimlanesOn || this.isEpicBoard
? sortBy([...Object.values(this.boardLists)], 'position')
: this.lists;
},
......
......@@ -112,7 +112,7 @@ export default {
this.listRef.removeEventListener('scroll', this.onScroll);
},
methods: {
...mapActions(['fetchIssuesForList', 'moveIssue']),
...mapActions(['fetchItemsForList', 'moveIssue']),
listHeight() {
return this.listRef.getBoundingClientRect().height;
},
......@@ -126,7 +126,7 @@ export default {
this.listRef.scrollTop = 0;
},
loadNextPage() {
this.fetchIssuesForList({ listId: this.list.id, fetchNext: true });
this.fetchItemsForList({ listId: this.list.id, fetchNext: true });
},
toggleForm() {
this.showIssueForm = !this.showIssueForm;
......
......@@ -79,7 +79,11 @@ export default {
}
},
fetchLists: ({ commit, state, dispatch }) => {
fetchLists: ({ dispatch }) => {
dispatch('fetchIssueLists');
},
fetchIssueLists: ({ commit, state, dispatch }) => {
const { boardType, filterParams, fullPath, boardId } = state;
const variables = {
......@@ -253,8 +257,8 @@ export default {
});
},
fetchIssuesForList: ({ state, commit }, { listId, fetchNext = false }) => {
commit(types.REQUEST_ISSUES_FOR_LIST, { listId, fetchNext });
fetchItemsForList: ({ state, commit }, { listId, fetchNext = false }) => {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { fullPath, boardId, boardType, filterParams } = state;
......@@ -281,9 +285,9 @@ export default {
const { lists } = data[boardType]?.board;
const listIssues = formatListIssues(lists);
const listPageInfo = formatListsPageInfo(lists);
commit(types.RECEIVE_ISSUES_FOR_LIST_SUCCESS, { listIssues, listPageInfo, listId });
commit(types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, { listIssues, listPageInfo, listId });
})
.catch(() => commit(types.RECEIVE_ISSUES_FOR_LIST_FAILURE, listId));
.catch(() => commit(types.RECEIVE_ITEMS_FOR_LIST_FAILURE, listId));
},
resetIssues: ({ commit }) => {
......
......@@ -14,9 +14,9 @@ export const MOVE_LIST = 'MOVE_LIST';
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
export const REMOVE_LIST = 'REMOVE_LIST';
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
export const REQUEST_ISSUES_FOR_LIST = 'REQUEST_ISSUES_FOR_LIST';
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 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 CREATE_ISSUE_FAILURE = 'CREATE_ISSUE_FAILURE';
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
......
......@@ -32,12 +32,13 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter
export default {
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
const { boardType, disabled, boardId, fullPath, boardConfig } = data;
const { boardType, disabled, boardId, fullPath, boardConfig, isEpicBoard } = data;
state.boardId = boardId;
state.fullPath = fullPath;
state.boardType = boardType;
state.disabled = disabled;
state.boardConfig = boardConfig;
state.isEpicBoard = isEpicBoard;
},
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => {
......@@ -103,14 +104,11 @@ export default {
state.boardLists = listsBackup;
},
[mutationTypes.REQUEST_ISSUES_FOR_LIST]: (state, { listId, fetchNext }) => {
[mutationTypes.REQUEST_ITEMS_FOR_LIST]: (state, { listId, fetchNext }) => {
Vue.set(state.listsFlags, listId, { [fetchNext ? 'isLoadingMore' : 'isLoading']: true });
},
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_SUCCESS]: (
state,
{ listIssues, listPageInfo, listId },
) => {
[mutationTypes.RECEIVE_ITEMS_FOR_LIST_SUCCESS]: (state, { listIssues, listPageInfo, listId }) => {
const { listData, issues } = listIssues;
Vue.set(state, 'issues', { ...state.issues, ...issues });
Vue.set(
......@@ -122,7 +120,7 @@ export default {
Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false });
},
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_FAILURE]: (state, listId) => {
[mutationTypes.RECEIVE_ITEMS_FOR_LIST_FAILURE]: (state, listId) => {
state.error = s__(
'Boards|An error occurred while fetching the board issues. Please reload the page.',
);
......
import { sortBy } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { urlParamsToObject } from '~/lib/utils/common_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import {
......@@ -25,6 +27,54 @@ export function fullUserId(userId) {
return `gid://gitlab/User/${userId}`;
}
export function fullEpicBoardId(epicBoardId) {
return `gid://gitlab/Boards::EpicBoard/${epicBoardId}`;
}
export function formatListEpics(listEpics) {
const epics = {};
let listEpicsCount;
const listData = listEpics.nodes.reduce((map, list) => {
// TODO update when list.epics.count is available: https://gitlab.com/gitlab-org/gitlab/-/issues/301017
listEpicsCount = list.epics.edges.length;
let sortedEpics = list.epics.edges.map((epicNode) => ({
...epicNode.node,
}));
sortedEpics = sortBy(sortedEpics, 'relativePosition');
return {
...map,
[list.id]: sortedEpics.map((i) => {
const id = getIdFromGraphQLId(i.id);
const listEpic = {
...i,
id,
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
};
epics[id] = listEpic;
return id;
}),
};
}, {});
return { listData, epics, listEpicsCount };
}
export function formatEpicListsPageInfo(lists) {
const listData = lists.nodes.reduce((map, list) => {
return {
...map,
[list.id]: list.epics.pageInfo,
};
}, {});
return listData;
}
export function transformBoardConfig(boardConfig) {
const updatedBoardConfig = {};
const passedFilterParams = urlParamsToObject(window.location.search);
......@@ -86,5 +136,6 @@ export default {
fullEpicId,
fullMilestoneId,
fullUserId,
fullEpicBoardId,
transformBoardConfig,
};
......@@ -75,7 +75,7 @@ export default {
},
},
methods: {
...mapActions(['moveList', 'fetchIssuesForList']),
...mapActions(['moveList', 'fetchItemsForList']),
handleDragOnEnd(params) {
const { newIndex, oldIndex, item, to } = params;
const { listId } = item.dataset;
......@@ -91,7 +91,7 @@ export default {
fetchMoreUnassignedIssues() {
this.lists.forEach((list) => {
if (this.pageInfoByListId[list.id]?.hasNextPage) {
this.fetchIssuesForList({ listId: list.id, fetchNext: true, noEpicIssues: true });
this.fetchItemsForList({ listId: list.id, fetchNext: true, noEpicIssues: true });
}
});
},
......
......@@ -78,7 +78,7 @@ export default {
filterParams: {
handler() {
if (this.isUnassignedIssuesLane) {
this.fetchIssuesForList({ listId: this.list.id, noEpicIssues: true });
this.fetchItemsForList({ listId: this.list.id, noEpicIssues: true });
}
},
deep: true,
......@@ -102,7 +102,7 @@ export default {
eventHub.$off(`toggle-issue-form-${this.list.id}`, this.toggleForm);
},
methods: {
...mapActions(['moveIssue', 'moveIssueEpic', 'fetchIssuesForList']),
...mapActions(['moveIssue', 'moveIssueEpic', 'fetchItemsForList']),
toggleForm() {
this.showIssueForm = !this.showIssueForm;
if (this.showIssueForm && this.isUnassignedIssuesLane) {
......
#import "~/graphql_shared/fragments/label.fragment.graphql"
query ListEpics($fullPath: ID!, $boardId: BoardsEpicBoardID!) {
group(fullPath: $fullPath) {
epicBoard(id: $boardId) {
lists {
nodes {
id
title
position
listType
label {
...Label
}
}
}
}
}
}
#import "~/graphql_shared/fragments/epic.fragment.graphql"
#import "~/graphql_shared/fragments/label.fragment.graphql"
query ListEpics(
$fullPath: ID!
$boardId: BoardsEpicBoardID!
$id: BoardsEpicListID
$after: String
$first: Int
) {
group(fullPath: $fullPath) {
epicBoard(id: $boardId) {
lists(id: $id) {
nodes {
id
epics(first: $first, after: $after) {
edges {
node {
...EpicNode
relativePosition
labels {
nodes {
...Label
}
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
}
}
......@@ -21,16 +21,24 @@ import {
urlParamsToObject,
} from '~/lib/utils/common_utils';
import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
import { fullEpicId } from '../boards_util';
import {
fullEpicId,
fullEpicBoardId,
formatListEpics,
formatEpicListsPageInfo,
} from '../boards_util';
import { EpicFilterType, IterationFilterType, GroupByParamType } from '../constants';
import epicQuery from '../graphql/epic.query.graphql';
import epicBoardListsQuery from '../graphql/epic_board_lists.query.graphql';
import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import issueSetEpicMutation from '../graphql/issue_set_epic.mutation.graphql';
import issueSetWeightMutation from '../graphql/issue_set_weight.mutation.graphql';
import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql';
import listsEpicsQuery from '../graphql/lists_epics.query.graphql';
import updateBoardEpicUserPreferencesMutation from '../graphql/updateBoardEpicUserPreferences.mutation.graphql';
import boardsStoreEE from './boards_store_ee';
import * as types from './mutation_types';
......@@ -72,6 +80,30 @@ const fetchAndFormatListIssues = (state, extraVariables) => {
});
};
const fetchAndFormatListEpics = (state, extraVariables) => {
const { fullPath, boardId, filterParams } = state;
const variables = {
fullPath,
boardId: fullEpicBoardId(boardId),
filters: { ...filterParams },
...extraVariables,
};
return gqlClient
.query({
query: listsEpicsQuery,
context: {
isSingleRequest: true,
},
variables,
})
.then(({ data }) => {
const { lists } = data.group?.epicBoard;
return { listEpics: formatListEpics(lists), listPageInfo: formatEpicListsPageInfo(lists) };
});
};
export default {
...actionsCE,
......@@ -115,7 +147,7 @@ export default {
commit(types.SET_FILTERS, filterParams);
},
performSearch({ dispatch, getters }) {
performSearch({ dispatch, getters, state }) {
dispatch(
'setFilters',
convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)),
......@@ -125,7 +157,7 @@ export default {
dispatch('resetEpics');
dispatch('resetIssues');
dispatch('fetchEpicsSwimlanes', {});
} else if (gon.features.graphqlBoardLists) {
} else if (gon.features.graphqlBoardLists || state.isEpicBoard) {
dispatch('fetchLists');
dispatch('resetIssues');
}
......@@ -267,8 +299,8 @@ export default {
notImplemented();
},
fetchIssuesForList: ({ state, commit }, { listId, fetchNext = false, noEpicIssues = false }) => {
commit(types.REQUEST_ISSUES_FOR_LIST, { listId, fetchNext });
fetchItemsForList: ({ state, commit }, { listId, fetchNext = false, noEpicIssues = false }) => {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { epicId, ...filterParams } = state.filterParams;
if (noEpicIssues && epicId !== undefined) {
......@@ -284,16 +316,31 @@ export default {
first: 20,
};
if (state.isEpicBoard) {
// This currently always fails. Epics will be loaded and displayed in the next iteration
// Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/233438
return fetchAndFormatListEpics(state, variables)
.then(({ listEpics, listPageInfo }) => {
commit(types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, {
listEpics,
listPageInfo,
listId,
noEpicIssues,
});
})
.catch(() => commit(types.RECEIVE_ITEMS_FOR_LIST_FAILURE, listId));
}
return fetchAndFormatListIssues(state, variables)
.then(({ listIssues, listPageInfo }) => {
commit(types.RECEIVE_ISSUES_FOR_LIST_SUCCESS, {
commit(types.RECEIVE_ITEMS_FOR_LIST_SUCCESS, {
listIssues,
listPageInfo,
listId,
noEpicIssues,
});
})
.catch(() => commit(types.RECEIVE_ISSUES_FOR_LIST_FAILURE, listId));
.catch(() => commit(types.RECEIVE_ITEMS_FOR_LIST_FAILURE, listId));
},
fetchIssuesForEpic: ({ state, commit }, epicId) => {
......@@ -478,4 +525,35 @@ export default {
commit(types.MOVE_ISSUE_FAILURE, { originalIssue, fromListId, toListId, originalIndex }),
);
},
fetchLists: ({ state, dispatch }) => {
const { isEpicBoard } = state;
if (!isEpicBoard) {
dispatch('fetchIssueLists');
} else {
dispatch('fetchEpicLists');
}
},
fetchEpicLists: ({ commit, state }) => {
const { filterParams, fullPath, boardId } = state;
const variables = {
fullPath,
boardId: fullEpicBoardId(boardId),
filters: filterParams,
};
return gqlClient
.query({
query: epicBoardListsQuery,
variables,
})
.then(({ data }) => {
const { lists } = data.group?.epicBoard;
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, formatBoardLists(lists));
})
.catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE));
},
};
......@@ -8,15 +8,16 @@ export const REQUEST_REMOVE_BOARD = 'REQUEST_REMOVE_BOARD';
export const RECEIVE_REMOVE_BOARD_SUCCESS = 'RECEIVE_REMOVE_BOARD_SUCCESS';
export const RECEIVE_REMOVE_BOARD_ERROR = 'RECEIVE_REMOVE_BOARD_ERROR';
export const TOGGLE_PROMOTION_STATE = 'TOGGLE_PROMOTION_STATE';
export const REQUEST_ISSUES_FOR_LIST = 'REQUEST_ISSUES_FOR_LIST';
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 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 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';
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
export const RECEIVE_SWIMLANES_FAILURE = 'RECEIVE_SWIMLANES_FAILURE';
......
......@@ -63,7 +63,7 @@ export default {
state.error = s__('Boards|An error occurred while updating the list. Please try again.');
},
[mutationTypes.RECEIVE_ISSUES_FOR_LIST_SUCCESS]: (
[mutationTypes.RECEIVE_ITEMS_FOR_LIST_SUCCESS]: (
state,
{ listIssues, listPageInfo, listId, noEpicIssues },
) => {
......
......@@ -12,4 +12,5 @@ export default () => ({
epicsCacheById: {},
epicFetchInProgress: false,
epicsFlags: {},
isEpicBoard: false,
});
......@@ -96,10 +96,14 @@ export default () => {
? parseInt($boardApp.dataset.boardWeight, 10)
: null,
},
isEpicBoard: true,
});
},
mounted() {
this.performSearch();
},
methods: {
...mapActions(['setInitialBoardData']),
...mapActions(['setInitialBoardData', 'performSearch']),
getNodes(data) {
return data[this.parent]?.board?.lists.nodes;
},
......
......@@ -7,6 +7,9 @@ class Groups::EpicBoardsController < Groups::ApplicationController
before_action :authorize_read_board!, only: [:index]
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:epic_boards, group, default_enabled: :yaml)
end
feature_category :boards
......
import { transformBoardConfig } from 'ee/boards/boards_util';
import {
formatListEpics,
formatEpicListsPageInfo,
transformBoardConfig,
} from 'ee/boards/boards_util';
import { mockLabel } from './mock_data';
const listId = 'gid://gitlab/Boards::EpicList/3';
describe('formatListEpics', () => {
it('formats raw response from list epics for state', () => {
const rawEpicsInLists = {
nodes: [
{
id: 'gid://gitlab/Boards::EpicList/3',
epics: {
edges: [
{
node: {
title: 'epic title',
id: 'gid://gitlab/Epic/1',
labels: {
nodes: [mockLabel],
},
},
},
],
},
},
],
};
const result = formatListEpics(rawEpicsInLists);
expect(result).toEqual({
epics: {
1: {
assignees: [],
id: 1,
labels: [mockLabel],
title: 'epic title',
},
},
listData: { [listId]: [1] },
listEpicsCount: 1,
});
});
});
describe('formatEpicListsPageInfo', () => {
it('formats raw pageInfo response from epics for state', () => {
const rawEpicsInListsPageInfo = {
nodes: [
{
id: listId,
epics: {
pageInfo: {
endCursor: 'MjA',
hasNextPage: true,
},
},
},
],
};
const result = formatEpicListsPageInfo(rawEpicsInListsPageInfo);
expect(result).toEqual({
[listId]: {
endCursor: 'MjA',
hasNextPage: true,
},
});
});
});
describe('transformBoardConfig', () => {
beforeEach(() => {
......
......@@ -3,6 +3,14 @@
import Vue from 'vue';
import '~/boards/models/list';
export const mockLabel = {
id: 'gid://gitlab/GroupLabel/121',
title: 'To Do',
color: '#F0AD4E',
textColor: '#FFFFFF',
description: null,
};
export const mockLists = [
{
id: 'gid://gitlab/List/1',
......@@ -22,13 +30,7 @@ export const mockLists = [
position: 0,
listType: 'label',
collapsed: false,
label: {
id: 'gid://gitlab/GroupLabel/121',
title: 'To Do',
color: '#F0AD4E',
textColor: '#FFFFFF',
description: null,
},
label: mockLabel,
maxIssueCount: 0,
assignee: null,
milestone: null,
......
......@@ -6,7 +6,7 @@ import boardsStoreEE from 'ee/boards/stores/boards_store_ee';
import * as types from 'ee/boards/stores/mutation_types';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { formatListIssues } from '~/boards/boards_util';
import { formatListIssues, formatBoardLists } from '~/boards/boards_util';
import * as typesCE from '~/boards/stores/mutation_types';
import * as commonUtils from '~/lib/utils/common_utils';
import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
......@@ -135,6 +135,75 @@ describe('performSearch', () => {
});
});
describe('fetchLists', () => {
it('should dispatch fetchIssueLists action when isEpicBoard is false on state', async () => {
await testAction({
action: actions.fetchLists,
state: { isEpicBoard: false },
expectedActions: [{ type: 'fetchIssueLists' }],
});
});
it('should dispatch fetchEpicLists action when isEpicBoard is true on state', async () => {
await testAction({
action: actions.fetchLists,
state: { isEpicBoard: true },
expectedActions: [{ type: 'fetchEpicLists' }],
});
});
});
describe('fetchEpicLists', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
filterParams: {},
};
const queryResponse = {
data: {
group: {
epicBoard: {
lists: {
nodes: mockLists,
},
},
},
},
};
const formattedLists = formatBoardLists(queryResponse.data.group.epicBoard.lists);
it('should commit mutations RECEIVE_BOARD_LISTS_SUCCESS on success', async () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
await testAction({
action: actions.fetchEpicLists,
state,
expectedMutations: [
{
type: types.RECEIVE_BOARD_LISTS_SUCCESS,
payload: formattedLists,
},
],
});
});
it('should commit mutations RECEIVE_BOARD_LISTS_FAILURE on failure', async () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
await testAction({
action: actions.fetchEpicLists,
state,
expectedMutations: [
{
type: types.RECEIVE_BOARD_LISTS_FAILURE,
},
],
});
});
});
describe('fetchEpicsSwimlanes', () => {
const state = {
fullPath: 'gitlab-org',
......@@ -425,7 +494,7 @@ describe('fetchIssuesForEpic', () => {
const formattedIssues = formatListIssues(queryResponse.data.group.board.lists);
it('should commit mutations REQUEST_ISSUES_FOR_EPIC and RECEIVE_ISSUES_FOR_LIST_SUCCESS on success', (done) => {
it('should commit mutations REQUEST_ISSUES_FOR_EPIC and RECEIVE_ITEMS_FOR_LIST_SUCCESS on success', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
......@@ -441,7 +510,7 @@ describe('fetchIssuesForEpic', () => {
);
});
it('should commit mutations REQUEST_ISSUES_FOR_EPIC and RECEIVE_ISSUES_FOR_LIST_FAILURE on failure', (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(
......
......@@ -11,7 +11,7 @@ const localVue = createLocalVue();
localVue.use(Vuex);
const actions = {
fetchIssuesForList: jest.fn(),
fetchItemsForList: jest.fn(),
};
const createStore = (state = defaultState) => {
......@@ -170,7 +170,7 @@ describe('Board list component', () => {
it('loads more issues after scrolling', () => {
wrapper.vm.listRef.dispatchEvent(new Event('scroll'));
expect(actions.fetchIssuesForList).toHaveBeenCalled();
expect(actions.fetchItemsForList).toHaveBeenCalled();
});
it('does not load issues if already loading', () => {
......@@ -179,7 +179,7 @@ describe('Board list component', () => {
});
wrapper.vm.listRef.dispatchEvent(new Event('scroll'));
expect(actions.fetchIssuesForList).not.toHaveBeenCalled();
expect(actions.fetchItemsForList).not.toHaveBeenCalled();
});
it('shows loading more spinner', async () => {
......
......@@ -112,6 +112,15 @@ describe('setActiveId', () => {
});
describe('fetchLists', () => {
it('should dispatch fetchIssueLists action', () => {
testAction({
action: actions.fetchLists,
expectedActions: [{ type: 'fetchIssueLists' }],
});
});
});
describe('fetchIssueLists', () => {
const state = {
fullPath: 'gitlab-org',
boardId: '1',
......@@ -138,7 +147,7 @@ describe('fetchLists', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchLists,
actions.fetchIssueLists,
{},
state,
[
......@@ -152,6 +161,23 @@ describe('fetchLists', () => {
);
});
it('should commit mutations RECEIVE_BOARD_LISTS_FAILURE on failure', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
testAction(
actions.fetchIssueLists,
{},
state,
[
{
type: types.RECEIVE_BOARD_LISTS_FAILURE,
},
],
[],
done,
);
});
it('dispatch createList action when backlog list does not exist and is not hidden', (done) => {
queryResponse = {
data: {
......@@ -168,7 +194,7 @@ describe('fetchLists', () => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchLists,
actions.fetchIssueLists,
{},
state,
[
......@@ -490,7 +516,7 @@ describe('removeList', () => {
});
});
describe('fetchIssuesForList', () => {
describe('fetchItemsForList', () => {
const listId = mockLists[0].id;
const state = {
......@@ -533,20 +559,20 @@ describe('fetchIssuesForList', () => {
[listId]: pageInfo,
};
it('should commit mutations REQUEST_ISSUES_FOR_LIST and RECEIVE_ISSUES_FOR_LIST_SUCCESS on success', (done) => {
it('should commit mutations REQUEST_ITEMS_FOR_LIST and RECEIVE_ITEMS_FOR_LIST_SUCCESS on success', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchIssuesForList,
actions.fetchItemsForList,
{ listId },
state,
[
{
type: types.REQUEST_ISSUES_FOR_LIST,
type: types.REQUEST_ITEMS_FOR_LIST,
payload: { listId, fetchNext: false },
},
{
type: types.RECEIVE_ISSUES_FOR_LIST_SUCCESS,
type: types.RECEIVE_ITEMS_FOR_LIST_SUCCESS,
payload: { listIssues: formattedIssues, listPageInfo, listId },
},
],
......@@ -555,19 +581,19 @@ describe('fetchIssuesForList', () => {
);
});
it('should commit mutations REQUEST_ISSUES_FOR_LIST and RECEIVE_ISSUES_FOR_LIST_FAILURE on failure', (done) => {
it('should commit mutations REQUEST_ITEMS_FOR_LIST and RECEIVE_ITEMS_FOR_LIST_FAILURE on failure', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
testAction(
actions.fetchIssuesForList,
actions.fetchItemsForList,
{ listId },
state,
[
{
type: types.REQUEST_ISSUES_FOR_LIST,
type: types.REQUEST_ITEMS_FOR_LIST,
payload: { listId, fetchNext: false },
},
{ type: types.RECEIVE_ISSUES_FOR_LIST_FAILURE, payload: listId },
{ type: types.RECEIVE_ITEMS_FOR_LIST_FAILURE, payload: listId },
],
[],
done,
......
......@@ -37,6 +37,7 @@ describe('Board Store Mutations', () => {
const boardConfig = {
milestoneTitle: 'Milestone 1',
};
const isEpicBoard = true;
mutations[types.SET_INITIAL_BOARD_DATA](state, {
boardId,
......@@ -44,6 +45,7 @@ describe('Board Store Mutations', () => {
boardType,
disabled,
boardConfig,
isEpicBoard,
});
expect(state.boardId).toEqual(boardId);
......@@ -51,6 +53,7 @@ describe('Board Store Mutations', () => {
expect(state.boardType).toEqual(boardType);
expect(state.disabled).toEqual(disabled);
expect(state.boardConfig).toEqual(boardConfig);
expect(state.isEpicBoard).toEqual(isEpicBoard);
});
});
......@@ -235,7 +238,7 @@ describe('Board Store Mutations', () => {
});
});
describe('RECEIVE_ISSUES_FOR_LIST_SUCCESS', () => {
describe('RECEIVE_ITEMS_FOR_LIST_SUCCESS', () => {
it('updates issuesByListId and issues on state', () => {
const listIssues = {
'gid://gitlab/List/1': [mockIssue.id],
......@@ -260,7 +263,7 @@ describe('Board Store Mutations', () => {
},
};
mutations.RECEIVE_ISSUES_FOR_LIST_SUCCESS(state, {
mutations.RECEIVE_ITEMS_FOR_LIST_SUCCESS(state, {
listIssues: { listData: listIssues, issues },
listPageInfo,
listId: 'gid://gitlab/List/1',
......@@ -271,7 +274,7 @@ describe('Board Store Mutations', () => {
});
});
describe('RECEIVE_ISSUES_FOR_LIST_FAILURE', () => {
describe('RECEIVE_ITEMS_FOR_LIST_FAILURE', () => {
it('sets error message', () => {
state = {
...state,
......@@ -281,7 +284,7 @@ describe('Board Store Mutations', () => {
const listId = 'gid://gitlab/List/1';
mutations.RECEIVE_ISSUES_FOR_LIST_FAILURE(state, listId);
mutations.RECEIVE_ITEMS_FOR_LIST_FAILURE(state, listId);
expect(state.error).toEqual(
'An error occurred while fetching the board issues. Please reload the page.',
......
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