Commit 864b69e0 authored by Wei-Meng Lee's avatar Wei-Meng Lee Committed by Mike Greiling

Test for right start/end dates in east timezone

Make use of timezone-mock to test for US/Pacific time
parent c2f3ac73
...@@ -8,19 +8,29 @@ export default class BurndownChartData { ...@@ -8,19 +8,29 @@ export default class BurndownChartData {
this.burndownEvents = this.processRawEvents(burndownEvents); this.burndownEvents = this.processRawEvents(burndownEvents);
// determine when to stop burndown chart // determine when to stop burndown chart
const today = dateFormat(new Date(), this.dateFormatMask); const today = dateFormat(Date.now(), this.dateFormatMask);
this.endDate = today < this.dueDate ? today : this.dueDate; this.endDate = today < this.dueDate ? today : this.dueDate;
// Make sure we get the burndown chart local start and end dates! new Date()
// and dateFormat() both convert the date at midnight UTC to the browser's
// timezone, leading to incorrect chart start and end points. Using
// new Date('YYYY-MM-DDTHH:MM:SS') gets the user's local date at midnight.
this.localStartDate = new Date(`${this.startDate}T00:00:00`);
this.localEndDate = new Date(`${this.endDate}T00:00:00`);
} }
generate() { generate() {
let openIssuesCount = 0; let openIssuesCount = 0;
let openIssuesWeight = 0; let openIssuesWeight = 0;
let carriedIssuesCount = 0;
let carriedIssuesWeight = 0;
const chartData = []; const chartData = [];
for ( for (
let date = new Date(this.startDate); let date = this.localStartDate;
date <= new Date(this.endDate); date <= this.localEndDate;
date.setDate(date.getDate() + 1) date.setDate(date.getDate() + 1)
) { ) {
const dateString = dateFormat(date, this.dateFormatMask); const dateString = dateFormat(date, this.dateFormatMask);
...@@ -38,6 +48,25 @@ export default class BurndownChartData { ...@@ -38,6 +48,25 @@ export default class BurndownChartData {
openIssuesCount += openedIssuesToday.count - closedIssuesToday.count; openIssuesCount += openedIssuesToday.count - closedIssuesToday.count;
openIssuesWeight += openedIssuesToday.weight - closedIssuesToday.weight; openIssuesWeight += openedIssuesToday.weight - closedIssuesToday.weight;
// Due to timezone differences or unforeseen bugs/errors in the source or
// processed data, it is possible that we end up with a negative issue or
// weight count on an given date. To mitigate this, we reset the current
// date's counters to 0 and carry forward the negative count to a future
// date until the total is positive again.
if (openIssuesCount < 0 || openIssuesWeight < 0) {
carriedIssuesCount = openIssuesCount;
carriedIssuesWeight = openIssuesWeight;
openIssuesCount = 0;
openIssuesWeight = 0;
} else if (carriedIssuesCount < 0 || carriedIssuesWeight < 0) {
openIssuesCount += carriedIssuesCount;
openIssuesWeight += carriedIssuesWeight;
carriedIssuesCount = 0;
carriedIssuesWeight = 0;
}
chartData.push([dateString, openIssuesCount, openIssuesWeight]); chartData.push([dateString, openIssuesCount, openIssuesWeight]);
} }
...@@ -46,14 +75,14 @@ export default class BurndownChartData { ...@@ -46,14 +75,14 @@ export default class BurndownChartData {
// Process raw milestone events: // Process raw milestone events:
// 1. Set event creation date to milestone start date if created before milestone start // 1. Set event creation date to milestone start date if created before milestone start
// 2. Convert event creation date to local timezone // 2. Convert event creation datetime to date in local timezone
processRawEvents(events) { processRawEvents(events) {
return events.map(event => ({ return events.map(event => ({
...event, ...event,
created_at: dateFormat( created_at:
new Date(event.created_at) < new Date(this.startDate) ? this.startDate : event.created_at, dateFormat(event.created_at, this.dateFormatMask) < this.startDate
this.dateFormatMask, ? this.startDate
), : dateFormat(event.created_at, this.dateFormatMask),
})); }));
} }
......
---
title: Fix x-axis burndown chart offset by timezone
merge_request: 15690
author:
type: fixed
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import timezoneMock from 'timezone-mock';
import BurndownChartData from 'ee/burndown_chart/burndown_chart_data'; import BurndownChartData from 'ee/burndown_chart/burndown_chart_data';
describe('BurndownChartData', () => { describe('BurndownChartData', () => {
...@@ -30,6 +31,24 @@ describe('BurndownChartData', () => { ...@@ -30,6 +31,24 @@ describe('BurndownChartData', () => {
]); ]);
}); });
describe('when viewing in a timezone in the west', () => {
beforeAll(() => {
timezoneMock.register('US/Pacific');
});
afterAll(() => {
timezoneMock.unregister();
});
it('has the right start and end dates', () => {
expect(burndownChartData.generate()).toEqual([
['2017-03-01', 1, 2],
['2017-03-02', 3, 6],
['2017-03-03', 3, 6],
]);
});
});
describe('when issues are created before start date', () => { describe('when issues are created before start date', () => {
beforeAll(() => { beforeAll(() => {
milestoneEvents.push({ milestoneEvents.push({
...@@ -49,22 +68,41 @@ describe('BurndownChartData', () => { ...@@ -49,22 +68,41 @@ describe('BurndownChartData', () => {
}); });
describe('when viewing before due date', () => { describe('when viewing before due date', () => {
const realDateNow = Date.now;
beforeAll(() => { beforeAll(() => {
const today = new Date(2017, 2, 2); const today = jest.fn(() => new Date(2017, 2, 2));
global.Date.now = today;
// eslint-disable-next-line no-global-assign });
Date = class extends Date {
constructor(date) { afterAll(() => {
super(date || today); global.Date.now = realDateNow;
}
};
}); });
it('counts until today if milestone due date > date today', () => { it('counts until today if milestone due date > date today', () => {
const chartData = burndownChartData.generate(); const chartData = burndownChartData.generate();
expect(dateFormat(new Date(), 'yyyy-mm-dd')).toEqual('2017-03-02');
expect(dateFormat(Date.now(), 'yyyy-mm-dd')).toEqual('2017-03-02');
expect(chartData[chartData.length - 1][0]).toEqual('2017-03-02'); expect(chartData[chartData.length - 1][0]).toEqual('2017-03-02');
}); });
}); });
describe('when first two days of milestone have negative issue count', () => {
beforeAll(() => {
milestoneEvents.push(
{ created_at: '2017-03-01T00:00:00.000Z', weight: 2, action: 'closed' },
{ created_at: '2017-03-01T00:00:00.000Z', weight: 2, action: 'closed' },
{ created_at: '2017-03-01T00:00:00.000Z', weight: 2, action: 'closed' },
);
});
it('sets first two dates data to 0 and carries forward negative total to the third day', () => {
expect(burndownChartData.generate()).toEqual([
['2017-03-01', 0, 0],
['2017-03-02', 0, 0],
['2017-03-03', 1, 2],
]);
});
});
}); });
}); });
...@@ -11815,6 +11815,11 @@ timers-browserify@^2.0.4: ...@@ -11815,6 +11815,11 @@ timers-browserify@^2.0.4:
dependencies: dependencies:
setimmediate "^1.0.4" setimmediate "^1.0.4"
timezone-mock@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/timezone-mock/-/timezone-mock-1.0.8.tgz#1b9f7af13f2bf84b7aa3d3d6e24aa17255b6037d"
integrity sha512-7dgx34HJPY8O/c5dbqG+I9S3TVDjrfssXmS8BNqiy8sdYvYDfM7shHpNA6VTDQWcDGyv43bE3El6YuFDQf1X3g==
tiny-emitter@^2.0.0: tiny-emitter@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
......
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