Commit afe8d6ae authored by Florie Guibert's avatar Florie Guibert

Swimlanes - Fetch issues using GraphQL endpoint

- Fetch issues for all board lists though VueX action
parent 1c45c9e2
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import ListIssue from 'ee_else_ce/boards/models/issue';
export function getMilestone() {
return null;
}
export function formatListIssues(listIssues) {
return listIssues.nodes.reduce((map, list) => {
return {
...map,
[list.id]: list.issues.nodes.map(
i =>
new ListIssue({
...i,
id: getIdFromGraphQLId(i.id),
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
}),
),
};
}, {});
}
export default {
getMilestone,
formatListIssues,
};
......@@ -42,7 +42,7 @@ export default {
},
},
computed: {
...mapState(['isShowingEpicsSwimlanes']),
...mapState(['isShowingEpicsSwimlanes', 'boardLists']),
isSwimlanesOn() {
return this.glFeatures.boardsWithSwimlanes && this.isShowingEpicsSwimlanes;
},
......@@ -73,11 +73,12 @@ export default {
<epics-swimlanes
v-else
ref="swimlanes"
:lists="lists"
:lists="boardLists"
:can-admin-list="canAdminList"
:disabled="disabled"
:board-id="boardId"
:group-id="groupId"
:root-path="rootPath"
/>
</div>
</template>
......@@ -117,7 +117,7 @@ export default () => {
boardId: this.boardId,
fullPath: $boardApp.dataset.fullPath,
};
this.setEndpoints(endpoints);
this.setInitialBoardData({ ...endpoints, boardType: this.parent });
boardsStore.setEndpoints(endpoints);
boardsStore.rootPath = this.boardsEndpoint;
......@@ -189,7 +189,7 @@ export default () => {
}
},
methods: {
...mapActions(['setEndpoints']),
...mapActions(['setInitialBoardData']),
updateTokens() {
this.filterManager.updateTokens();
},
......
......@@ -60,7 +60,9 @@ class List {
this.title = this.milestone.title;
}
if (!typeInfo.isBlank && this.id) {
// doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards
// Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/229416
if (!typeInfo.isBlank && this.id && !obj.doNotFetchIssues) {
this.getIssues().catch(() => {
// TODO: handle request error
});
......
......@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
position
listType
collapsed
maxIssueCount
label {
id
title
......
#import "./issue.fragment.graphql"
query GroupListIssues($fullPath: ID!, $boardId: ID!) {
group(fullPath: $fullPath) {
board(id: $boardId) {
lists {
nodes {
id
issues {
nodes {
...IssueNode
}
}
}
}
}
}
}
#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
epic {
id
}
assignees {
nodes {
...User
}
}
labels {
nodes {
id
title
color
description
}
}
}
#import "./issue.fragment.graphql"
query ProjectListIssues($fullPath: ID!, $boardId: ID!) {
project(fullPath: $fullPath) {
board(id: $boardId) {
lists {
nodes {
id
issues {
nodes {
...IssueNode
}
}
}
}
}
}
}
import * as types from './mutation_types';
import createDefaultClient from '~/lib/graphql';
import { BoardType } from '~/boards/constants';
import { formatListIssues } from '../boards_util';
import groupListsIssuesQuery from '../queries/group_lists_issues.query.graphql';
import projectListsIssuesQuery from '../queries/project_lists_issues.query.graphql';
const gqlClient = createDefaultClient();
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
......@@ -6,8 +13,8 @@ const notImplemented = () => {
};
export default {
setEndpoints: ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints);
setInitialBoardData: ({ commit }, data) => {
commit(types.SET_INITIAL_BOARD_DATA, data);
},
setActiveId({ commit }, id) {
......@@ -38,6 +45,32 @@ export default {
notImplemented();
},
fetchIssuesForAllLists: ({ state, commit }) => {
commit(types.REQUEST_ISSUES_FOR_ALL_LISTS);
const { endpoints, boardType } = state;
const { fullPath, boardId } = endpoints;
const query = boardType === BoardType.group ? groupListsIssuesQuery : projectListsIssuesQuery;
const variables = {
fullPath,
boardId: `gid://gitlab/Board/${boardId}`,
};
return gqlClient
.query({
query,
variables,
})
.then(({ data }) => {
const { lists } = data[boardType]?.board;
const listIssues = formatListIssues(lists);
commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS, listIssues);
})
.catch(() => commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE));
},
moveIssue: () => {
notImplemented();
},
......
......@@ -81,7 +81,7 @@ const boardsStore = {
showPage(page) {
this.state.currentPage = page;
},
addList(listObj) {
updateListPosition(listObj) {
const listType = listObj.listType || listObj.list_type;
let { position } = listObj;
if (listType === ListType.closed) {
......@@ -91,6 +91,10 @@ const boardsStore = {
}
const list = new List({ ...listObj, position });
return list;
},
addList(listObj) {
const list = this.updateListPosition(listObj);
this.state.lists = sortBy([...this.state.lists, list], 'position');
return list;
},
......@@ -850,19 +854,28 @@ const boardsStore = {
},
refreshIssueData(issue, obj) {
issue.id = obj.id;
issue.iid = obj.iid;
issue.title = obj.title;
issue.confidential = obj.confidential;
issue.dueDate = obj.due_date;
issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
issue.referencePath = obj.reference_path;
issue.path = obj.real_path;
issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
// issue.id = obj.id;
// issue.iid = obj.iid;
// issue.title = obj.title;
// issue.confidential = obj.confidential;
// issue.dueDate = obj.due_date || obj.dueDate;
// issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
// issue.referencePath = obj.reference_path || obj.referencePath;
// issue.path = obj.real_path || obj.webUrl;
// issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
// issue.project_id = obj.project_id;
// issue.timeEstimate = obj.time_estimate || obj.timeEstimate;
// issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
// issue.blocked = obj.blocked;
// issue.epic = obj.epic;
const convertedObj = convertObjectPropsToCamelCase(obj, {
dropKeys: ['issue_sidebar_endpoint', 'real_path', 'webUrl'],
});
convertedObj.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
issue.path = obj.real_path || obj.webUrl;
issue.project_id = obj.project_id;
issue.timeEstimate = obj.time_estimate;
issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
issue.blocked = obj.blocked;
Object.assign(issue, convertedObj);
if (obj.project) {
issue.project = new IssueProject(obj.project);
......
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
......@@ -8,6 +8,9 @@ export const RECEIVE_UPDATE_LIST_ERROR = 'RECEIVE_UPDATE_LIST_ERROR';
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_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';
export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR';
......
......@@ -6,8 +6,10 @@ const notImplemented = () => {
};
export default {
[mutationTypes.SET_ENDPOINTS]: (state, endpoints) => {
[mutationTypes.SET_INITIAL_BOARD_DATA]: (state, data) => {
const { boardType, ...endpoints } = data;
state.endpoints = endpoints;
state.boardType = boardType;
},
[mutationTypes.SET_ACTIVE_ID](state, id) {
......@@ -50,6 +52,20 @@ export default {
notImplemented();
},
[mutationTypes.REQUEST_ISSUES_FOR_ALL_LISTS]: state => {
state.isLoadingIssues = true;
},
[mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, listIssues) => {
state.issuesByListId = listIssues;
state.isLoadingIssues = false;
},
[mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE]: state => {
state.listIssueFetchFailure = true;
state.isLoadingIssues = false;
},
[mutationTypes.REQUEST_ADD_ISSUE]: () => {
notImplemented();
},
......
......@@ -2,6 +2,10 @@ import { inactiveId } from '~/boards/constants';
export default () => ({
endpoints: {},
boardType: null,
isShowingLabels: true,
activeId: inactiveId,
issuesByListId: {},
isLoadingIssues: false,
listIssueFetchFailure: false,
});
......@@ -598,17 +598,17 @@
$epic-icons-spacing: 40px;
.board-epic-lane {
max-width: calc(100vw - #{$contextual-sidebar-width} - $epic-icons-spacing);
max-width: calc(100vw - #{$contextual-sidebar-width} - #{$epic-icons-spacing});
.page-with-icon-sidebar & {
max-width: calc(100vw - #{$contextual-sidebar-collapsed-width} - $epic-icons-spacing);
max-width: calc(100vw - #{$contextual-sidebar-collapsed-width} - #{$epic-icons-spacing});
}
.page-with-icon-sidebar .is-compact & {
max-width: calc(100vw - #{$contextual-sidebar-collapsed-width} - #{$gutter-width} - $epic-icons-spacing);
max-width: calc(100vw - #{$contextual-sidebar-collapsed-width} - #{$gutter-width} - #{$epic-icons-spacing});
}
.is-compact & {
max-width: calc(100vw - #{$contextual-sidebar-width} - #{$gutter-width} - $epic-icons-spacing);
max-width: calc(100vw - #{$contextual-sidebar-width} - #{$gutter-width} - #{$epic-icons-spacing});
}
}
......@@ -27,6 +27,23 @@ export default {
type: Array,
required: true,
},
issues: {
type: Object,
required: true,
},
isLoadingIssues: {
type: Boolean,
required: false,
default: false,
},
disabled: {
type: Boolean,
required: true,
},
rootPath: {
type: String,
required: true,
},
},
data() {
return {
......@@ -73,10 +90,12 @@ export default {
},
},
methods: {
epicIssuesForList(listIssues) {
return this.epic.issues.filter(epicIssue =>
Boolean(listIssues.find(listIssue => String(listIssue.iid) === epicIssue.iid)),
);
epicIssuesForList(listId) {
if (this.issues[listId]) {
return this.issues[listId].filter(issue => issue.epic && issue.epic.id === this.epic.id);
}
return [];
},
toggleExpanded() {
this.isExpanded = !this.isExpanded;
......@@ -137,7 +156,10 @@ export default {
v-for="list in lists"
:key="`${list.id}-issues`"
:list="list"
:issues="epicIssuesForList(list.issues)"
:issues="epicIssuesForList(list.id)"
:is-loading="isLoadingIssues"
:disabled="disabled"
:root-path="rootPath"
/>
</div>
</div>
......
<script>
import { mapState } from 'vuex';
import { mapActions, mapState } from 'vuex';
import { n__ } from '~/locale';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
......@@ -39,14 +39,30 @@ export default {
required: false,
default: 0,
},
rootPath: {
type: String,
required: true,
},
},
computed: {
...mapState(['epics']),
issuesCount() {
return this.lists.reduce((total, list) => total + list.issues.length, 0);
...mapState(['epics', 'issuesByListId', 'isLoadingIssues']),
unassignedIssuesCount() {
return this.lists.reduce((total, list) => total + this.unassignedIssues(list).length, 0);
},
unassignedIssuesCountTooltipText() {
return n__(`%d unassigned issue`, `%d unassigned issues`, this.unassignedIssuesCount);
},
},
issuesCountTooltipText() {
return n__(`%d unassigned issue`, `%d unassigned issues`, this.issuesCount);
mounted() {
this.fetchIssuesForAllLists();
},
methods: {
...mapActions(['fetchIssuesForAllLists']),
unassignedIssues(list) {
if (this.issuesByListId[list.id]) {
return this.issuesByListId[list.id].filter(i => i.epic === null);
}
return [];
},
},
};
......@@ -78,7 +94,16 @@ export default {
</div>
</div>
<div class="board-epics-swimlanes gl-display-table">
<epic-lane v-for="epic in epics" :key="epic.id" :epic="epic" :lists="lists" />
<epic-lane
v-for="epic in epics"
:key="epic.id"
:epic="epic"
:lists="lists"
:issues="issuesByListId"
:is-loading-issues="isLoadingIssues"
:disabled="disabled"
:root-path="rootPath"
/>
<div class="board-lane-unassigned-issues gl-sticky gl-display-inline-block gl-left-0">
<div class="gl-left-0 gl-py-5 gl-px-3 gl-display-flex gl-align-items-center">
<span
......@@ -88,14 +113,14 @@ export default {
</span>
<span
v-gl-tooltip.hover
:title="issuesCountTooltipText"
:title="unassignedIssuesCountTooltipText"
class="gl-display-flex gl-align-items-center gl-text-gray-700"
tabindex="0"
:aria-label="issuesCountTooltipText"
:aria-label="unassignedIssuesCountTooltipText"
data-testid="issues-lane-issue-count"
>
<gl-icon class="gl-mr-2 gl-flex-shrink-0" name="issues" aria-hidden="true" />
<span aria-hidden="true">{{ issuesCount }}</span>
<span aria-hidden="true">{{ unassignedIssuesCount }}</span>
</span>
</div>
</div>
......@@ -104,9 +129,12 @@ export default {
v-for="list in lists"
:key="`${list.id}-issues`"
:list="list"
:issues="list.issues"
:issues="unassignedIssues(list)"
:group-id="groupId"
:is-unassigned-issues-lane="true"
:is-loading="isLoadingIssues"
:disabled="disabled"
:root-path="rootPath"
/>
</div>
</div>
......
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '~/boards/eventhub';
import BoardCard from '~/boards/components/board_card.vue';
import BoardNewIssue from '~/boards/components/board_new_issue.vue';
......@@ -7,12 +8,17 @@ export default {
components: {
BoardCard,
BoardNewIssue,
GlLoadingIcon,
},
props: {
list: {
type: Object,
required: true,
},
disabled: {
type: Boolean,
required: true,
},
issues: {
type: Array,
required: true,
......@@ -27,6 +33,15 @@ export default {
required: false,
default: false,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
rootPath: {
type: String,
required: true,
},
},
data() {
return {
......@@ -56,6 +71,7 @@ export default {
:class="{ 'is-collapsed': !list.isExpanded }"
>
<div class="board-inner gl-rounded-base gl-relative gl-w-full">
<gl-loading-icon v-if="isLoading" class="gl-p-2" />
<board-new-issue
v-if="list.type !== 'closed' && showIssueForm && isUnassignedIssuesLane"
:group-id="groupId"
......@@ -69,6 +85,7 @@ export default {
:index="index"
:list="list"
:issue="issue"
:root-path="rootPath"
/>
</ul>
</div>
......
import axios from 'axios';
import { sortBy } from 'lodash';
import boardsStore from '~/boards/stores/boards_store';
import actionsCE from '~/boards/stores/actions';
import boardsStoreEE from './boards_store_ee';
import * as types from './mutation_types';
......@@ -14,7 +16,7 @@ const notImplemented = () => {
const gqlClient = createDefaultClient();
const fetchEpicsSwimlanes = ({ endpoints }) => {
const fetchEpicsSwimlanes = ({ endpoints, boardType }) => {
const { fullPath, boardId } = endpoints;
const query = epicsSwimlanes;
......@@ -29,7 +31,8 @@ const fetchEpicsSwimlanes = ({ endpoints }) => {
variables,
})
.then(({ data }) => {
return data;
const { lists } = data[boardType]?.board;
return lists?.nodes;
});
};
......@@ -101,33 +104,25 @@ export default {
notImplemented();
},
toggleEpicSwimlanes: ({ state, commit, dispatch }) => {
toggleEpicSwimlanes: ({ state, commit }) => {
commit(types.TOGGLE_EPICS_SWIMLANES);
if (state.isShowingEpicsSwimlanes) {
Promise.all([fetchEpicsSwimlanes(state), fetchEpics(state)])
.then(([swimlanes, epics]) => {
if (swimlanes) {
dispatch('receiveSwimlanesSuccess', swimlanes);
.then(([lists, epics]) => {
if (lists) {
let boardLists = lists.map(list =>
boardsStore.updateListPosition({ ...list, doNotFetchIssues: true }),
);
boardLists = sortBy([...boardLists], 'position');
commit(types.RECEIVE_BOARD_LISTS_SUCCESS, boardLists);
}
if (epics) {
dispatch('receiveEpicsSuccess', epics);
commit(types.RECEIVE_EPICS_SUCCESS, epics);
}
})
.catch(() => dispatch('receiveSwimlanesFailure'));
.catch(() => commit(types.RECEIVE_SWIMLANES_FAILURE));
}
},
receiveSwimlanesSuccess: ({ commit }, swimlanes) => {
commit(types.RECEIVE_SWIMLANES_SUCCESS, swimlanes);
},
receiveSwimlanesFailure: ({ commit }) => {
commit(types.RECEIVE_SWIMLANES_FAILURE);
},
receiveEpicsSuccess: ({ commit }, swimlanes) => {
commit(types.RECEIVE_EPICS_SUCCESS, swimlanes);
},
};
......@@ -12,7 +12,7 @@ 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 TOGGLE_EPICS_SWIMLANES = 'TOGGLE_EPICS_SWIMLANES';
export const RECEIVE_SWIMLANES_SUCCESS = 'RECEIVE_SWIMLANES_SUCCESS';
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
export const RECEIVE_SWIMLANES_FAILURE = 'RECEIVE_SWIMLANES_FAILURE';
export const RECEIVE_EPICS_SUCCESS = 'RECEIVE_EPICS_SUCCESS';
export const SET_SHOW_LABELS = 'SET_SHOW_LABELS';
......@@ -69,8 +69,8 @@ export default {
state.epicsSwimlanesFetchInProgress = true;
},
[mutationTypes.RECEIVE_SWIMLANES_SUCCESS]: (state, swimlanes) => {
state.epicsSwimlanes = swimlanes;
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, boardLists) => {
state.boardLists = boardLists;
state.epicsSwimlanesFetchInProgress = false;
},
......
......@@ -8,4 +8,5 @@ export default () => ({
epicsSwimlanesFetchFailure: false,
epicsSwimlanes: {},
epics: {},
boardLists: [],
});
......@@ -10,6 +10,7 @@ describe('BoardList Component', () => {
path: '/test',
},
real_path: '',
webUrl: '',
};
const componentProps = {
......
......@@ -19,9 +19,19 @@ describe('EpicLane', () => {
});
const createComponent = (props = {}) => {
const issues = mockLists.reduce((map, list) => {
return {
...map,
[list.id]: mockIssues,
};
}, {});
const defaultProps = {
epic: mockEpic,
lists: mockLists.map(listMock => Vue.observable(new List(listMock))),
issues,
disabled: false,
rootPath: '/',
};
wrapper = shallowMount(EpicLane, {
......
......@@ -51,6 +51,8 @@ describe('IssuesLaneList', () => {
propsData: {
list,
issues: mockIssues,
disabled: false,
rootPath: '/',
},
});
};
......
......@@ -99,29 +99,3 @@ describe('toggleEpicSwimlanes', () => {
);
});
});
describe('receiveSwimlanesSuccess', () => {
it('should commit mutation RECEIVE_SWIMLANES_SUCCESS', done => {
testAction(
actions.receiveSwimlanesSuccess,
{},
{},
[{ type: types.RECEIVE_SWIMLANES_SUCCESS, payload: {} }],
[],
done,
);
});
});
describe('receiveSwimlanesFailure', () => {
it('should commit mutation RECEIVE_SWIMLANES_SUCCESS', done => {
testAction(
actions.receiveSwimlanesFailure,
null,
{},
[{ type: types.RECEIVE_SWIMLANES_FAILURE }],
[],
done,
);
});
});
......@@ -103,17 +103,17 @@ describe('TOGGLE_EPICS_SWIMLANES', () => {
});
});
describe('RECEIVE_SWIMLANES_SUCCESS', () => {
it('sets epicsSwimlanesFetchInProgress to false and populates epicsSwimlanes with payload', () => {
describe('RECEIVE_BOARD_LISTS_SUCCESS', () => {
it('sets epicsSwimlanesFetchInProgress to false and populates boardLists with payload', () => {
const state = {
epicsSwimlanesFetchInProgress: true,
epicsSwimlanes: {},
boardLists: {},
};
mutations.RECEIVE_SWIMLANES_SUCCESS(state, mockLists);
mutations.RECEIVE_BOARD_LISTS_SUCCESS(state, mockLists);
expect(state.epicsSwimlanesFetchInProgress).toBe(false);
expect(state.epicsSwimlanes).toEqual(mockLists);
expect(state.boardLists).toEqual(mockLists);
});
});
......
......@@ -5,7 +5,7 @@ import '~/boards/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
import boardsStore from '~/boards/stores/boards_store';
import { setMockEndpoints } from './mock_data';
import { setMockEndpoints, mockIssue } from './mock_data';
describe('Issue model', () => {
let issue;
......@@ -14,28 +14,7 @@ describe('Issue model', () => {
setMockEndpoints();
boardsStore.create();
issue = new ListIssue({
title: 'Testing',
id: 1,
iid: 1,
confidential: false,
labels: [
{
id: 1,
title: 'test',
color: 'red',
description: 'testing',
},
],
assignees: [
{
id: 1,
name: 'name',
username: 'username',
avatar_url: 'http://avatar_url',
},
],
});
issue = new ListIssue(mockIssue);
});
it('has label', () => {
......
......@@ -92,6 +92,29 @@ export const mockMilestone = {
due_date: '2019-12-31',
};
export const mockIssue = {
title: 'Testing',
id: 1,
iid: 1,
confidential: false,
labels: [
{
id: 1,
title: 'test',
color: 'red',
description: 'testing',
},
],
assignees: [
{
id: 1,
name: 'name',
username: 'username',
avatar_url: 'http://avatar_url',
},
],
};
export const BoardsMockData = {
GET: {
'/test/-/boards/1/lists/300/issues?id=300&page=1': {
......
......@@ -9,18 +9,18 @@ const expectNotImplemented = action => {
});
};
describe('setEndpoints', () => {
it('sets endpoints object', () => {
const mockEndpoints = {
describe('setInitialBoardData', () => {
it('sets data object', () => {
const mockData = {
foo: 'bar',
bar: 'baz',
};
return testAction(
actions.setEndpoints,
mockEndpoints,
actions.setInitialBoardData,
mockData,
{},
[{ type: types.SET_ENDPOINTS, payload: mockEndpoints }],
[{ type: types.SET_INITIAL_BOARD_DATA, payload: mockData }],
[],
);
});
......
import mutations from '~/boards/stores/mutations';
import * as types from '~/boards/stores/mutation_types';
import defaultState from '~/boards/stores/state';
import { mockIssue } from '../mock_data';
const expectNotImplemented = action => {
it('is not implemented', () => {
......@@ -15,7 +15,7 @@ describe('Board Store Mutations', () => {
state = defaultState();
});
describe('SET_ENDPOINTS', () => {
describe('SET_INITIAL_BOARD_DATA', () => {
it('Should set initial Boards data to state', () => {
const endpoints = {
boardsEndpoint: '/boards/',
......@@ -25,15 +25,17 @@ describe('Board Store Mutations', () => {
boardId: 1,
fullPath: 'gitlab-org',
};
const boardType = 'group';
mutations[types.SET_ENDPOINTS](state, endpoints);
mutations.SET_INITIAL_BOARD_DATA(state, { ...endpoints, boardType });
expect(state.endpoints).toEqual(endpoints);
expect(state.boardType).toEqual(boardType);
});
});
describe('SET_ACTIVE_ID', () => {
it('updates aciveListId to be the value that is passed', () => {
it('updates activeListId to be the value that is passed', () => {
const expectedId = 1;
mutations.SET_ACTIVE_ID(state, expectedId);
......@@ -78,6 +80,35 @@ describe('Board Store Mutations', () => {
expectNotImplemented(mutations.RECEIVE_REMOVE_LIST_ERROR);
});
describe('REQUEST_ISSUES_FOR_ALL_LISTS', () => {
it('sets isLoadingIssues to true', () => {
expect(state.isLoadingIssues).toBe(false);
mutations.REQUEST_ISSUES_FOR_ALL_LISTS(state);
expect(state.isLoadingIssues).toBe(true);
});
});
describe('RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS', () => {
it('sets isLoadingIssues to false and updates issuesByListId object', () => {
const listIssues = {
'1': [mockIssue],
};
state = {
...state,
isLoadingIssues: true,
issuesByListId: {},
};
mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS(state, listIssues);
expect(state.isLoadingIssues).toBe(false);
expect(state.issuesByListId).toEqual(listIssues);
});
});
describe('REQUEST_ADD_ISSUE', () => {
expectNotImplemented(mutations.REQUEST_ADD_ISSUE);
});
......
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