Skip to content

Commit 05e176f

Browse files
authored
Merge pull request #3558 from obsidian-tasks-group/test-issue-3553
refactor: Add failing test and prepare to fix #3553
2 parents 6a2821b + 8807f1e commit 05e176f

File tree

4 files changed

+72
-25
lines changed

4 files changed

+72
-25
lines changed

src/Task/Occurrence.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Moment } from 'moment/moment';
22
import { compareByDate } from '../DateTime/DateTools';
3+
import { getSettings } from '../Config/Settings';
34

45
/**
56
* A set of dates on a single instance of {@link Recurrence}.
@@ -11,6 +12,20 @@ export class Occurrence {
1112
public readonly scheduledDate: Moment | null;
1213
public readonly dueDate: Moment | null;
1314

15+
constructor({
16+
startDate = null,
17+
scheduledDate = null,
18+
dueDate = null,
19+
}: {
20+
startDate?: Moment | null;
21+
scheduledDate?: Moment | null;
22+
dueDate?: Moment | null;
23+
}) {
24+
this.startDate = startDate ?? null;
25+
this.scheduledDate = scheduledDate ?? null;
26+
this.dueDate = dueDate ?? null;
27+
}
28+
1429
/**
1530
* The reference date is used to calculate future occurrences.
1631
*
@@ -24,21 +39,8 @@ export class Occurrence {
2439
* same relative distance to the due date as the original task. For example
2540
* "starts one week before it is due".
2641
*/
27-
public readonly referenceDate: Moment | null;
28-
29-
constructor({
30-
startDate = null,
31-
scheduledDate = null,
32-
dueDate = null,
33-
}: {
34-
startDate?: Moment | null;
35-
scheduledDate?: Moment | null;
36-
dueDate?: Moment | null;
37-
}) {
38-
this.startDate = startDate ?? null;
39-
this.scheduledDate = scheduledDate ?? null;
40-
this.dueDate = dueDate ?? null;
41-
this.referenceDate = this.getReferenceDate();
42+
public get referenceDate(): moment.Moment | null {
43+
return this.getReferenceDate();
4244
}
4345

4446
/**
@@ -87,9 +89,8 @@ export class Occurrence {
8789
* If the occurrence has no reference date, an empty {@link Occurrence} will be returned.
8890
*
8991
* @param nextReferenceDate
90-
* @param removeScheduledDate - Optional boolean to remove the scheduled date from the next occurrence so long as a start or due date exists.
9192
*/
92-
public next(nextReferenceDate: Date, removeScheduledDate: boolean = false): Occurrence {
93+
public next(nextReferenceDate: Date): Occurrence {
9394
// Only if a reference date is given. A reference date will exist if at
9495
// least one of the other dates is set.
9596
if (this.referenceDate === null) {
@@ -103,7 +104,9 @@ export class Occurrence {
103104
const hasStartDate = this.startDate !== null;
104105
const hasDueDate = this.dueDate !== null;
105106
const canRemoveScheduledDate = hasStartDate || hasDueDate;
106-
const shouldRemoveScheduledDate = removeScheduledDate && canRemoveScheduledDate;
107+
108+
const { removeScheduledDateOnRecurrence } = getSettings();
109+
const shouldRemoveScheduledDate = removeScheduledDateOnRecurrence && canRemoveScheduledDate;
107110

108111
const startDate = this.nextOccurrenceDate(this.startDate, nextReferenceDate);
109112
const scheduledDate = shouldRemoveScheduledDate

src/Task/Recurrence.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,15 @@ export class Recurrence {
7272
* Returns the dates of the next occurrence or null if there is no next occurrence.
7373
*
7474
* @param today - Optional date representing the completion date. Defaults to today.
75-
* @param removeScheduledDate - Optional boolean to remove the scheduled date from the next occurrence so long as a start or due date exists.
7675
*/
77-
public next(today = window.moment(), removeScheduledDate: boolean = false): Occurrence | null {
76+
public next(today = window.moment()): Occurrence | null {
7877
const nextReferenceDate = this.nextReferenceDate(today);
7978

8079
if (nextReferenceDate === null) {
8180
return null;
8281
}
8382

84-
return this.occurrence.next(nextReferenceDate, removeScheduledDate);
83+
return this.occurrence.next(nextReferenceDate);
8584
}
8685

8786
public identicalTo(other: Recurrence) {

src/Task/Task.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,7 @@ export class Task extends ListItem {
377377
return [toggledTask];
378378
}
379379

380-
const { removeScheduledDateOnRecurrence } = getSettings();
381-
const nextOccurrence = this.recurrence.next(today, removeScheduledDateOnRecurrence);
380+
const nextOccurrence = this.recurrence.next(today);
382381
if (nextOccurrence === null) {
383382
return [toggledTask];
384383
}

tests/Task/Recurrence.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import moment from 'moment';
55
import { Occurrence } from '../../src/Task/Occurrence';
66
import { Recurrence } from '../../src/Task/Recurrence';
77
import { RecurrenceBuilder } from '../TestingTools/RecurrenceBuilder';
8+
import { updateSettings } from '../../src/Config/Settings';
89

910
window.moment = moment;
1011

@@ -230,6 +231,14 @@ describe('identicalTo', () => {
230231
});
231232

232233
describe('Recurrence - with removeScheduledDateOnRecurrence', () => {
234+
beforeEach(() => {
235+
updateSettings({ removeScheduledDateOnRecurrence: true });
236+
});
237+
238+
afterEach(() => {
239+
updateSettings({ removeScheduledDateOnRecurrence: false });
240+
});
241+
233242
it('should remove the scheduledDate when removeScheduledDate is true', () => {
234243
// Arrange
235244
const recurrence = Recurrence.fromText({
@@ -242,7 +251,7 @@ describe('Recurrence - with removeScheduledDateOnRecurrence', () => {
242251
});
243252

244253
// Act
245-
const next = recurrence!.next(undefined, true);
254+
const next = recurrence!.next();
246255

247256
// Assert
248257
expect(next!.startDate).toEqualMoment(moment('2022-02-01'));
@@ -260,9 +269,46 @@ describe('Recurrence - with removeScheduledDateOnRecurrence', () => {
260269
});
261270

262271
// Act
263-
const next = recurrence!.next(undefined, true);
272+
const next = recurrence!.next();
264273

265274
// Assert
266275
expect(next!.scheduledDate).not.toBeNull();
267276
});
277+
278+
describe('dropScheduledDate and when done', () => {
279+
beforeEach(() => {
280+
jest.useFakeTimers();
281+
jest.setSystemTime(new Date('2022-01-10'));
282+
});
283+
afterEach(() => {
284+
jest.useRealTimers();
285+
});
286+
287+
it.failing('calculates correct start date with "dropScheduledDate" and "when done", with no due date', () => {
288+
// Arrange
289+
290+
// The task is being completed on the 10th of January.
291+
// And the user has turned on the setting to remove the scheduled date in the new instance.
292+
// This means that the user will apply a scheduled date at some point in the future,
293+
// when they are ready to work on the task.
294+
// Therefore, in the new task, as there is no Due date, the only date the user will see
295+
// is the Start date.
296+
// And so it makes sense to calculate the new 'happens' date using the Start date,
297+
// not the old scheduled date.
298+
const recurrence = Recurrence.fromText({
299+
recurrenceRuleText: 'every 3 days when done',
300+
occurrence: new Occurrence({
301+
startDate: moment('2022-01-01').startOf('day'),
302+
scheduledDate: moment('2022-01-04').startOf('day'),
303+
}),
304+
});
305+
306+
// Act
307+
const next = recurrence!.next();
308+
309+
// Assert
310+
expect(next!.startDate).toEqualMoment(moment('2022-01-13'));
311+
expect(next!.scheduledDate).toBeNull();
312+
});
313+
});
268314
});

0 commit comments

Comments
 (0)