mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-04-14 19:47:03 +00:00
* WIP: #12555 -dayOfMonth takes timezone into account on task update for monthly dailys -startDate set to start of day in user's tz on task update for monthly dailys -tweaked a test so that it's actually testing something * WIP: 12555 -task.startDate gets set to start of day in user's tz on task creation and task update -set preferences.timezoneOffset to nonzero value in certian test user objects -removed date literals in test:api-v3:integration:tasks:PUT
This commit is contained in:
parent
e8d3090a99
commit
db3d233ae5
8 changed files with 83 additions and 29 deletions
|
|
@ -8,9 +8,14 @@ import {
|
|||
|
||||
describe('POST /tasks/user', () => {
|
||||
let user;
|
||||
let tzoffset;
|
||||
|
||||
before(async () => {
|
||||
tzoffset = new Date().getTimezoneOffset();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
user = await generateUser({ 'preferences.timezoneOffset': tzoffset });
|
||||
});
|
||||
|
||||
context('validates params', async () => {
|
||||
|
|
@ -544,7 +549,7 @@ describe('POST /tasks/user', () => {
|
|||
expect(task.everyX).to.eql(5);
|
||||
expect(task.daysOfMonth).to.eql([15]);
|
||||
expect(task.weeksOfMonth).to.eql([3]);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
expect(new Date(task.startDate)).to.eql(new Date(now.setHours(0, 0, 0, 0)));
|
||||
expect(task.isDue).to.be.true;
|
||||
expect(task.nextDue.length).to.eql(6);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@ import {
|
|||
|
||||
describe('PUT /tasks/:id', () => {
|
||||
let user;
|
||||
let tzoffset;
|
||||
|
||||
before(async () => {
|
||||
tzoffset = (new Date()).getTimezoneOffset();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser();
|
||||
user = await generateUser({ 'preferences.timezoneOffset': tzoffset });
|
||||
});
|
||||
|
||||
context('validates params', () => {
|
||||
|
|
@ -503,7 +508,8 @@ describe('PUT /tasks/:id', () => {
|
|||
let monthly;
|
||||
|
||||
beforeEach(async () => {
|
||||
const date1 = moment.utc('2020-07-01').toDate();
|
||||
// using date literals is discouraged here, daylight savings will break everything
|
||||
const date1 = moment().toDate();
|
||||
monthly = await user.post('/tasks/user', {
|
||||
text: 'test monthly',
|
||||
type: 'daily',
|
||||
|
|
@ -514,7 +520,7 @@ describe('PUT /tasks/:id', () => {
|
|||
});
|
||||
|
||||
it('updates days of month when start date updated', async () => {
|
||||
const date2 = moment.utc('2020-07-01').toDate();
|
||||
const date2 = moment().add(6, 'months').toDate();
|
||||
const savedMonthly = await user.put(`/tasks/${monthly._id}`, {
|
||||
startDate: date2,
|
||||
});
|
||||
|
|
@ -523,18 +529,30 @@ describe('PUT /tasks/:id', () => {
|
|||
});
|
||||
|
||||
it('updates next due when start date updated', async () => {
|
||||
const date2 = moment.utc('2022-07-01').toDate();
|
||||
const date2 = moment().add(6, 'months').toDate();
|
||||
const savedMonthly = await user.put(`/tasks/${monthly._id}`, {
|
||||
startDate: date2,
|
||||
});
|
||||
|
||||
expect(savedMonthly.nextDue.length).to.eql(6);
|
||||
expect(moment(savedMonthly.nextDue[0]).toDate()).to.eql(moment.utc('2022-08-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[1]).toDate()).to.eql(moment.utc('2022-09-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[2]).toDate()).to.eql(moment.utc('2022-10-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[3]).toDate()).to.eql(moment.utc('2022-11-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[4]).toDate()).to.eql(moment.utc('2022-12-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[5]).toDate()).to.eql(moment.utc('2023-01-01').toDate());
|
||||
expect(moment(savedMonthly.nextDue[0]).toDate()).to.eql(
|
||||
moment(date2).add(1, 'months').startOf('day').toDate(),
|
||||
);
|
||||
expect(moment(savedMonthly.nextDue[1]).toDate()).to.eql(
|
||||
moment(date2).add(2, 'months').startOf('day').toDate(),
|
||||
);
|
||||
expect(moment(savedMonthly.nextDue[2]).toDate()).to.eql(
|
||||
moment(date2).add(3, 'months').startOf('day').toDate(),
|
||||
);
|
||||
expect(moment(savedMonthly.nextDue[3]).toDate()).to.eql(
|
||||
moment(date2).add(4, 'months').startOf('day').toDate(),
|
||||
);
|
||||
expect(moment(savedMonthly.nextDue[4]).toDate()).to.eql(
|
||||
moment(date2).add(5, 'months').startOf('day').toDate(),
|
||||
);
|
||||
expect(moment(savedMonthly.nextDue[5]).toDate()).to.eql(
|
||||
moment(date2).add(6, 'months').startOf('day').toDate(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,18 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
|||
let user;
|
||||
let guild;
|
||||
let challenge;
|
||||
let tzoffset;
|
||||
|
||||
function findUserChallengeTask (memberTask) {
|
||||
return memberTask.challenge.id === challenge._id;
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
tzoffset = new Date().getTimezoneOffset();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({ balance: 1 });
|
||||
user = await generateUser({ balance: 1, 'preferences.timezoneOffset': tzoffset });
|
||||
guild = await generateGroup(user);
|
||||
challenge = await generateChallenge(user, guild);
|
||||
await user.post(`/challenges/${challenge._id}/join`);
|
||||
|
|
@ -165,7 +170,7 @@ describe('POST /tasks/challenge/:challengeId', () => {
|
|||
expect(task.type).to.eql('daily');
|
||||
expect(task.frequency).to.eql('daily');
|
||||
expect(task.everyX).to.eql(5);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
expect(new Date(task.startDate)).to.eql(new Date(now.setHours(0, 0, 0, 0)));
|
||||
|
||||
expect(userChallengeTask.notes).to.eql(task.notes);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe('PUT /tasks/:id', () => {
|
|||
let challenge;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
user = await generateUser({ 'preferences.timezoneOffset': new Date().getTimezoneOffset() });
|
||||
guild = await generateGroup(user);
|
||||
challenge = await generateChallenge(user, guild);
|
||||
await user.post(`/challenges/${challenge._id}/join`);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,18 @@ import {
|
|||
describe('POST /tasks/group/:groupid', () => {
|
||||
let user; let guild; let
|
||||
manager;
|
||||
let tzoffset;
|
||||
const groupName = 'Test Public Guild';
|
||||
const groupType = 'guild';
|
||||
|
||||
before(async () => {
|
||||
tzoffset = new Date().getTimezoneOffset();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({ balance: 1 });
|
||||
// user = await generateUser({ balance: 1, 'preferences.timezoneOffset': tzoffset });
|
||||
const { group, groupLeader, members } = await createAndPopulateGroup({
|
||||
leaderDetails: { balance: 10, 'preferences.timezoneOffset': tzoffset },
|
||||
groupDetails: {
|
||||
name: groupName,
|
||||
type: groupType,
|
||||
|
|
@ -128,7 +134,7 @@ describe('POST /tasks/group/:groupid', () => {
|
|||
expect(task.type).to.eql('daily');
|
||||
expect(task.frequency).to.eql('daily');
|
||||
expect(task.everyX).to.eql(5);
|
||||
expect(new Date(task.startDate)).to.eql(now);
|
||||
expect(new Date(task.startDate)).to.eql(new Date(now.setHours(0, 0, 0, 0)));
|
||||
});
|
||||
|
||||
it('allows a manager to add a group task', async () => {
|
||||
|
|
|
|||
|
|
@ -678,18 +678,29 @@ api.updateTask = {
|
|||
task.group.managerNotes = sanitizedObj.managerNotes;
|
||||
}
|
||||
|
||||
// If the daily task was set to repeat monthly on a day of the month, and the start date was
|
||||
// updated, the task will then need to be updated to repeat on the same day of the month as the
|
||||
// new start date. For example, if the start date is updated to 7/2/2020, the daily should
|
||||
// repeat on the 2nd day of the month. It's possible that a task can repeat monthly on a
|
||||
// week of the month, in which case we won't update the repetition at all.
|
||||
if (
|
||||
task.type === 'daily'
|
||||
&& task.frequency === 'monthly'
|
||||
&& task.daysOfMonth.length
|
||||
&& task.startDate
|
||||
// For daily tasks, update start date based on timezone to maintain consistency
|
||||
if (task.type === 'daily'
|
||||
&& task.startDate
|
||||
) {
|
||||
task.daysOfMonth = [moment(task.startDate).date()];
|
||||
task.startDate = moment(task.startDate).utcOffset(
|
||||
-user.preferences.timezoneOffset,
|
||||
).startOf('day').toDate();
|
||||
|
||||
// If the daily task was set to repeat monthly on a day of the month, and the start date was
|
||||
// updated, the task will then need to be updated to repeat on the same day of the month as
|
||||
// the new start date. For example, if the start date is updated to 7/2/2020, the daily
|
||||
// should repeat on the 2nd day of the month. It's possible that a task can repeat monthly
|
||||
// on a week of the month, in which case we won't update the repetition at all.
|
||||
if (
|
||||
task.frequency === 'monthly'
|
||||
&& task.daysOfMonth.length
|
||||
) {
|
||||
// We also need to aware of the user's timezone. Start date is represented as UTC, so the
|
||||
// start date and day of month might be different
|
||||
task.daysOfMonth = [moment(task.startDate).utcOffset(
|
||||
-user.preferences.timezoneOffset,
|
||||
).date()];
|
||||
}
|
||||
}
|
||||
|
||||
setNextDue(task, user);
|
||||
|
|
|
|||
|
|
@ -124,6 +124,15 @@ export async function createTasks (req, res, options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
// set startDate to midnight in the user's timezone
|
||||
if (taskType === 'daily') {
|
||||
const awareStartDate = moment(newTask.startDate).utcOffset(-user.preferences.timezoneOffset);
|
||||
if (awareStartDate.format('HMsS') !== '0000') {
|
||||
awareStartDate.startOf('day');
|
||||
newTask.startDate = awareStartDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
setNextDue(newTask, user);
|
||||
|
||||
// Validate that the task is valid and throw if it isn't
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ export const DailySchema = new Schema(_.defaults({
|
|||
startDate: {
|
||||
$type: Date,
|
||||
default () {
|
||||
return moment().startOf('day').toDate();
|
||||
return moment.utc().toDate();
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue