Commit 6d08e79d authored by Florie Guibert's avatar Florie Guibert

Refactor roadmap utils

Refactor epic specific roadmap utils to prepare adding milestones to
roadmap
parent e50a6361
......@@ -3,6 +3,7 @@ import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import * as epicUtils from '../utils/epic_utils';
import * as roadmapItemUtils from '../utils/roadmap_item_utils';
import {
getEpicsPathForPreset,
getEpicsTimeframeRange,
......@@ -76,7 +77,7 @@ export const receiveEpicsSuccess = (
{ rawEpics, newEpic, timeframeExtended },
) => {
const epics = rawEpics.reduce((filteredEpics, epic) => {
const formattedEpic = epicUtils.formatEpicDetails(
const formattedEpic = roadmapItemUtils.formatRoadmapItemDetails(
epic,
getters.timeframeStartDate,
getters.timeframeEndDate,
......@@ -186,7 +187,11 @@ export const extendTimeframe = ({ commit, state, getters }, { extendAs }) => {
export const refreshEpicDates = ({ commit, state, getters }) => {
const epics = state.epics.map(epic =>
epicUtils.processEpicDates(epic, getters.timeframeStartDate, getters.timeframeEndDate),
roadmapItemUtils.processRoadmapItemDates(
epic,
getters.timeframeStartDate,
getters.timeframeEndDate,
),
);
commit(types.SET_EPICS, epics);
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { newDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
export const gqClient = createGqClient(
......@@ -9,104 +7,6 @@ export const gqClient = createGqClient(
},
);
/**
* Updates provided `epic` object with necessary props
* representing underlying dates.
*
* @param {Object} epic
* @param {Date} timeframeStartDate
* @param {Date} timeframeEndDate
*/
export const processEpicDates = (epic, timeframeStartDate, timeframeEndDate) => {
if (!epic.startDateUndefined) {
// If startDate is less than first timeframe item
if (epic.originalStartDate.getTime() < timeframeStartDate.getTime()) {
Object.assign(epic, {
// startDate is out of range
startDateOutOfRange: true,
// Use startDate object to set a proxy date so
// that timeline bar can render it.
startDate: newDate(timeframeStartDate),
});
} else {
Object.assign(epic, {
// startDate is within range
startDateOutOfRange: false,
// Set startDate to original startDate
startDate: newDate(epic.originalStartDate),
});
}
} else {
Object.assign(epic, {
startDate: newDate(timeframeStartDate),
});
}
if (!epic.endDateUndefined) {
// If endDate is greater than last timeframe item
if (epic.originalEndDate.getTime() > timeframeEndDate.getTime()) {
Object.assign(epic, {
// endDate is out of range
endDateOutOfRange: true,
// Use endDate object to set a proxy date so
// that timeline bar can render it.
endDate: newDate(timeframeEndDate),
});
} else {
Object.assign(epic, {
// startDate is within range
endDateOutOfRange: false,
// Set startDate to original startDate
endDate: newDate(epic.originalEndDate),
});
}
} else {
Object.assign(epic, {
endDate: newDate(timeframeEndDate),
});
}
return epic;
};
/**
* Constructs Epic object with camelCase props and assigns proxy dates in case
* start or end dates are unavailable.
*
* @param {Object} rawEpic
* @param {Date} timeframeStartDate
* @param {Date} timeframeEndDate
*/
export const formatEpicDetails = (rawEpic, timeframeStartDate, timeframeEndDate) => {
const epicItem = convertObjectPropsToCamelCase(rawEpic);
const rawStartDate = rawEpic.start_date || rawEpic.startDate;
const rawEndDate = rawEpic.end_date || rawEpic.dueDate;
if (rawStartDate) {
// If startDate is present
const startDate = parsePikadayDate(rawStartDate);
epicItem.startDate = startDate;
epicItem.originalStartDate = startDate;
} else {
// startDate is not available
epicItem.startDateUndefined = true;
}
if (rawEndDate) {
// If endDate is present
const endDate = parsePikadayDate(rawEndDate);
epicItem.endDate = endDate;
epicItem.originalEndDate = endDate;
} else {
// endDate is not available
epicItem.endDateUndefined = true;
}
processEpicDates(epicItem, timeframeStartDate, timeframeEndDate);
return epicItem;
};
/**
* Returns array of epics extracted from GraphQL response
* discarding the `edges`->`node` nesting
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { newDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
/**
* Updates provided `epic` or `milestone` object with necessary props
* representing underlying dates.
*
* @param {Object} roadmapItem (epic or milestone)
* @param {Date} timeframeStartDate
* @param {Date} timeframeEndDate
*/
export const processRoadmapItemDates = (roadmapItem, timeframeStartDate, timeframeEndDate) => {
if (!roadmapItem.startDateUndefined) {
// If startDate is less than first timeframe item
if (roadmapItem.originalStartDate.getTime() < timeframeStartDate.getTime()) {
Object.assign(roadmapItem, {
// startDate is out of range
startDateOutOfRange: true,
// Use startDate object to set a proxy date so
// that timeline bar can render it.
startDate: newDate(timeframeStartDate),
});
} else {
Object.assign(roadmapItem, {
// startDate is within range
startDateOutOfRange: false,
// Set startDate to original startDate
startDate: newDate(roadmapItem.originalStartDate),
});
}
} else {
Object.assign(roadmapItem, {
startDate: newDate(timeframeStartDate),
});
}
if (!roadmapItem.endDateUndefined) {
// If endDate is greater than last timeframe item
if (roadmapItem.originalEndDate.getTime() > timeframeEndDate.getTime()) {
Object.assign(roadmapItem, {
// endDate is out of range
endDateOutOfRange: true,
// Use endDate object to set a proxy date so
// that timeline bar can render it.
endDate: newDate(timeframeEndDate),
});
} else {
Object.assign(roadmapItem, {
// startDate is within range
endDateOutOfRange: false,
// Set startDate to original startDate
endDate: newDate(roadmapItem.originalEndDate),
});
}
} else {
Object.assign(roadmapItem, {
endDate: newDate(timeframeEndDate),
});
}
return roadmapItem;
};
/**
* Constructs Epic or Milstone object with camelCase props and assigns proxy dates in case
* start or end dates are unavailable.
*
* @param {Object} rawRoadmapItem (epic or milestone)
* @param {Date} timeframeStartDate
* @param {Date} timeframeEndDate
*/
export const formatRoadmapItemDetails = (rawRoadmapItem, timeframeStartDate, timeframeEndDate) => {
const roadmapItem = convertObjectPropsToCamelCase(rawRoadmapItem);
const rawStartDate = rawRoadmapItem.start_date || rawRoadmapItem.startDate;
const rawEndDate = rawRoadmapItem.end_date || rawRoadmapItem.dueDate;
if (rawStartDate) {
// If startDate is present
const startDate = parsePikadayDate(rawStartDate);
roadmapItem.startDate = startDate;
roadmapItem.originalStartDate = startDate;
} else {
// startDate is not available
roadmapItem.startDateUndefined = true;
}
if (rawEndDate) {
// If endDate is present
const endDate = parsePikadayDate(rawEndDate);
roadmapItem.endDate = endDate;
roadmapItem.originalEndDate = endDate;
} else {
// endDate is not available
roadmapItem.endDateUndefined = true;
}
processRoadmapItemDates(roadmapItem, timeframeStartDate, timeframeEndDate);
return roadmapItem;
};
......@@ -6,6 +6,7 @@ import * as types from 'ee/roadmap/store/mutation_types';
import defaultState from 'ee/roadmap/store/state';
import { getTimeframeForMonthsView } from 'ee/roadmap/utils/roadmap_utils';
import * as epicUtils from 'ee/roadmap/utils/epic_utils';
import * as roadmapItemUtils from 'ee/roadmap/utils/roadmap_item_utils';
import { PRESET_TYPES, EXTEND_AS } from 'ee/roadmap/constants';
import groupEpics from 'ee/roadmap/queries/groupEpics.query.graphql';
import epicChildEpics from 'ee/roadmap/queries/epicChildEpics.query.graphql';
......@@ -386,7 +387,11 @@ describe('Roadmap Vuex Actions', () => {
describe('refreshEpicDates', () => {
it('should update epics after refreshing epic dates to match with updated timeframe', done => {
const epics = rawEpics.map(epic =>
epicUtils.formatEpicDetails(epic, state.timeframeStartDate, state.timeframeEndDate),
roadmapItemUtils.formatRoadmapItemDetails(
epic,
state.timeframeStartDate,
state.timeframeEndDate,
),
);
testAction(
......
import * as epicUtils from 'ee/roadmap/utils/epic_utils';
import { parsePikadayDate } from '~/lib/utils/datetime_utility';
import { rawEpics, mockGroupEpicsQueryResponse } from '../mock_data';
describe('processEpicDates', () => {
const timeframeStartDate = new Date(2017, 0, 1);
const timeframeEndDate = new Date(2017, 11, 31);
it('Should set `startDateOutOfRange`/`endDateOutOfRange` as true and `startDate` and `endDate` to dates of timeframe range when epic dates are outside timeframe range', () => {
const mockEpic = {
originalStartDate: new Date(2016, 11, 15),
originalEndDate: new Date(2018, 0, 1),
};
const updatedEpic = epicUtils.processEpicDates(mockEpic, timeframeStartDate, timeframeEndDate);
expect(updatedEpic.startDateOutOfRange).toBe(true);
expect(updatedEpic.endDateOutOfRange).toBe(true);
expect(updatedEpic.startDate.getTime()).toBe(timeframeStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(timeframeEndDate.getTime());
});
it('Should set `startDateOutOfRange`/`endDateOutOfRange` as false and `startDate` and `endDate` to actual epic dates when they are within timeframe range', () => {
const mockEpic = {
originalStartDate: new Date(2017, 2, 10),
originalEndDate: new Date(2017, 6, 22),
};
const updatedEpic = epicUtils.processEpicDates(mockEpic, timeframeStartDate, timeframeEndDate);
expect(updatedEpic.startDateOutOfRange).toBe(false);
expect(updatedEpic.endDateOutOfRange).toBe(false);
expect(updatedEpic.startDate.getTime()).toBe(mockEpic.originalStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(mockEpic.originalEndDate.getTime());
});
it('Should set `startDate` and `endDate` to timeframe start and end dates when epic dates are undefined', () => {
const mockEpic = {
startDateUndefined: true,
endDateUndefined: true,
};
const updatedEpic = epicUtils.processEpicDates(mockEpic, timeframeStartDate, timeframeEndDate);
expect(updatedEpic.startDate.getTime()).toBe(timeframeStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(timeframeEndDate.getTime());
});
});
describe('formatEpicDetails', () => {
const timeframeStartDate = new Date(2017, 0, 1);
const timeframeEndDate = new Date(2017, 11, 31);
const rawEpic = rawEpics[0];
it('Should return formatted Epic object from raw Epic object', () => {
const epic = epicUtils.formatEpicDetails(rawEpic, timeframeStartDate, timeframeEndDate);
expect(epic.id).toBe(rawEpic.id);
expect(epic.name).toBe(rawEpic.name);
expect(epic.groupId).toBe(rawEpic.group_id);
expect(epic.groupName).toBe(rawEpic.group_name);
});
it('Should return formatted Epic object with `startDate`/`endDate` and `originalStartDate`/originalEndDate` initialized when dates are present', () => {
const mockRawEpic = {
start_date: '2017-2-15',
end_date: '2017-7-22',
};
const epic = epicUtils.formatEpicDetails(mockRawEpic, timeframeStartDate, timeframeEndDate);
const startDate = parsePikadayDate(mockRawEpic.start_date);
const endDate = parsePikadayDate(mockRawEpic.end_date);
expect(epic.startDate.getTime()).toBe(startDate.getTime());
expect(epic.originalStartDate.getTime()).toBe(startDate.getTime());
expect(epic.endDate.getTime()).toBe(endDate.getTime());
expect(epic.originalEndDate.getTime()).toBe(endDate.getTime());
});
it('Should return formatted Epic object with `startDateUndefined`/startDateUndefined` set to true when dates are null/undefined', () => {
const epic = epicUtils.formatEpicDetails({}, timeframeStartDate, timeframeEndDate);
expect(epic.originalStartDate).toBeUndefined();
expect(epic.originalEndDate).toBeUndefined();
expect(epic.startDateUndefined).toBe(true);
expect(epic.endDateUndefined).toBe(true);
});
});
import { mockGroupEpicsQueryResponse } from '../mock_data';
describe('extractGroupEpics', () => {
it('returns array of epics with `edges->nodes` nesting removed', () => {
......
import * as roadmapItemUtils from 'ee/roadmap/utils/roadmap_item_utils';
import { parsePikadayDate } from '~/lib/utils/datetime_utility';
import { rawEpics } from '../mock_data';
describe('processRoadmapItemDates', () => {
const timeframeStartDate = new Date(2017, 0, 1);
const timeframeEndDate = new Date(2017, 11, 31);
it('Should set `startDateOutOfRange`/`endDateOutOfRange` as true and `startDate` and `endDate` to dates of timeframe range when epic dates are outside timeframe range', () => {
const mockEpic = {
originalStartDate: new Date(2016, 11, 15),
originalEndDate: new Date(2018, 0, 1),
};
const updatedEpic = roadmapItemUtils.processRoadmapItemDates(
mockEpic,
timeframeStartDate,
timeframeEndDate,
);
expect(updatedEpic.startDateOutOfRange).toBe(true);
expect(updatedEpic.endDateOutOfRange).toBe(true);
expect(updatedEpic.startDate.getTime()).toBe(timeframeStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(timeframeEndDate.getTime());
});
it('Should set `startDateOutOfRange`/`endDateOutOfRange` as false and `startDate` and `endDate` to actual epic dates when they are within timeframe range', () => {
const mockEpic = {
originalStartDate: new Date(2017, 2, 10),
originalEndDate: new Date(2017, 6, 22),
};
const updatedEpic = roadmapItemUtils.processRoadmapItemDates(
mockEpic,
timeframeStartDate,
timeframeEndDate,
);
expect(updatedEpic.startDateOutOfRange).toBe(false);
expect(updatedEpic.endDateOutOfRange).toBe(false);
expect(updatedEpic.startDate.getTime()).toBe(mockEpic.originalStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(mockEpic.originalEndDate.getTime());
});
it('Should set `startDate` and `endDate` to timeframe start and end dates when epic dates are undefined', () => {
const mockEpic = {
startDateUndefined: true,
endDateUndefined: true,
};
const updatedEpic = roadmapItemUtils.processRoadmapItemDates(
mockEpic,
timeframeStartDate,
timeframeEndDate,
);
expect(updatedEpic.startDate.getTime()).toBe(timeframeStartDate.getTime());
expect(updatedEpic.endDate.getTime()).toBe(timeframeEndDate.getTime());
});
});
describe('formatRoadmapItemDetails', () => {
const timeframeStartDate = new Date(2017, 0, 1);
const timeframeEndDate = new Date(2017, 11, 31);
const rawEpic = rawEpics[0];
it('Should return formatted Epic object from raw Epic object', () => {
const epic = roadmapItemUtils.formatRoadmapItemDetails(
rawEpic,
timeframeStartDate,
timeframeEndDate,
);
expect(epic.id).toBe(rawEpic.id);
expect(epic.name).toBe(rawEpic.name);
expect(epic.groupId).toBe(rawEpic.group_id);
expect(epic.groupName).toBe(rawEpic.group_name);
});
it('Should return formatted Epic object with `startDate`/`endDate` and `originalStartDate`/originalEndDate` initialized when dates are present', () => {
const mockRawEpic = {
start_date: '2017-2-15',
end_date: '2017-7-22',
};
const epic = roadmapItemUtils.formatRoadmapItemDetails(
mockRawEpic,
timeframeStartDate,
timeframeEndDate,
);
const startDate = parsePikadayDate(mockRawEpic.start_date);
const endDate = parsePikadayDate(mockRawEpic.end_date);
expect(epic.startDate.getTime()).toBe(startDate.getTime());
expect(epic.originalStartDate.getTime()).toBe(startDate.getTime());
expect(epic.endDate.getTime()).toBe(endDate.getTime());
expect(epic.originalEndDate.getTime()).toBe(endDate.getTime());
});
it('Should return formatted Epic object with `startDateUndefined`/startDateUndefined` set to true when dates are null/undefined', () => {
const epic = roadmapItemUtils.formatRoadmapItemDetails(
{},
timeframeStartDate,
timeframeEndDate,
);
expect(epic.originalStartDate).toBeUndefined();
expect(epic.originalEndDate).toBeUndefined();
expect(epic.startDateUndefined).toBe(true);
expect(epic.endDateUndefined).toBe(true);
});
});
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