1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
import { describe, expect, it } from "vitest";
import { computeNextSchedule, type ScheduleInput } from "./fsrs";
const baseCard: ScheduleInput = {
state: 0,
due: new Date("2026-05-01T00:00:00Z"),
stability: 0,
difficulty: 0,
elapsedDays: 0,
scheduledDays: 0,
reps: 0,
lapses: 0,
lastReview: null,
};
describe("computeNextSchedule", () => {
it("schedules a new card forward when rated Good", () => {
const now = new Date("2026-05-02T10:00:00Z");
const result = computeNextSchedule(baseCard, 3, now);
expect(result.reps).toBe(1);
expect(result.lastReview.getTime()).toBe(now.getTime());
expect(result.due.getTime()).toBeGreaterThan(now.getTime());
expect(result.stability).toBeGreaterThan(0);
expect(result.difficulty).toBeGreaterThan(0);
});
it("counts a lapse when a Review-state card is rated Again", () => {
const card: ScheduleInput = {
state: 2,
due: new Date("2026-05-01T00:00:00Z"),
stability: 10,
difficulty: 5,
elapsedDays: 5,
scheduledDays: 5,
reps: 3,
lapses: 0,
lastReview: new Date("2026-04-26T00:00:00Z"),
};
const now = new Date("2026-05-02T10:00:00Z");
const result = computeNextSchedule(card, 1, now);
expect(result.lapses).toBe(1);
});
it("computes reviewElapsedDays from previous lastReview", () => {
const lastReview = new Date("2026-04-29T00:00:00Z");
const card: ScheduleInput = {
...baseCard,
state: 2,
stability: 5,
difficulty: 5,
lastReview,
reps: 1,
};
const now = new Date("2026-05-02T00:00:00Z");
const result = computeNextSchedule(card, 3, now);
expect(result.reviewElapsedDays).toBe(3);
});
it("uses 0 reviewElapsedDays for a card without lastReview", () => {
const now = new Date("2026-05-02T00:00:00Z");
const result = computeNextSchedule(baseCard, 3, now);
expect(result.reviewElapsedDays).toBe(0);
});
it("higher ratings yield longer scheduled intervals than lower ratings", () => {
const card: ScheduleInput = {
state: 2,
due: new Date("2026-05-01T00:00:00Z"),
stability: 10,
difficulty: 5,
elapsedDays: 10,
scheduledDays: 10,
reps: 5,
lapses: 0,
lastReview: new Date("2026-04-21T00:00:00Z"),
};
const now = new Date("2026-05-02T00:00:00Z");
const hard = computeNextSchedule(card, 2, now);
const good = computeNextSchedule(card, 3, now);
const easy = computeNextSchedule(card, 4, now);
expect(easy.scheduledDays).toBeGreaterThanOrEqual(good.scheduledDays);
expect(good.scheduledDays).toBeGreaterThanOrEqual(hard.scheduledDays);
});
});
|