aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/island/calendar/test_cal.js
blob: 9d0d14759cdb1f63e976a0c823318906b9a510bb (plain)
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// Run: node desktop/island/calendar/test_cal.js
// Cal.js is a QML library, so its .pragma line is stripped first.

const fs = require("fs");
const assert = require("assert");

const src = fs.readFileSync(__dirname + "/Cal.js", "utf8").replace(/^\.pragma library$/m, "");
const C = new Function(src + "\nreturn { pad, addDays, today, daysBetween, monthCells, shiftMonth," +
    " lastDay, onDay, dayEvents, dots, timeLabel, validTime, stepTime, newDraft, fromEvent, pick," +
    " toPayload };")();

// Dates are plain YYYY-MM-DD strings, arithmetic in UTC so DST never moves a day.
assert.strictEqual(C.addDays("2026-10-31", 1), "2026-11-01");
assert.strictEqual(C.addDays("2026-03-01", -1), "2026-02-28");
assert.strictEqual(C.addDays("2026-10-24", 2), "2026-10-26");
assert.strictEqual(C.daysBetween("2026-10-30", "2026-11-02"), 3);
assert.strictEqual(C.today(new Date(2026, 9, 13, 23, 59)), "2026-10-13");

// Month cells: six weeks from Monday. October 2026 starts on a Thursday.
const oct = C.monthCells(2026, 9);
assert.strictEqual(oct.length, 42);
assert.strictEqual(oct[0].iso, "2026-09-28");
assert.strictEqual(oct[0].inMonth, false);
assert.strictEqual(oct[3].iso, "2026-10-01");
assert.strictEqual(oct[3].inMonth, true);
assert.strictEqual(oct[3].day, 1);
assert.strictEqual(oct[41].iso, "2026-11-08");
// June 2026 starts on a Monday: no leading days.
assert.strictEqual(C.monthCells(2026, 5)[0].iso, "2026-06-01");

assert.deepStrictEqual(C.shiftMonth(2026, 11, 1), { y: 2027, m0: 0 });
assert.deepStrictEqual(C.shiftMonth(2026, 0, -1), { y: 2025, m0: 11 });

// Which days an event covers.
const timed = { uid: "t", calendar: "home", allDay: false, start: "2026-10-15T14:30", end: "2026-10-15T15:30" };
const night = { uid: "n", calendar: "home", allDay: false, start: "2026-10-15T22:00", end: "2026-10-16T00:00" };
const late = { uid: "l", calendar: "home", allDay: false, start: "2026-10-15T23:00", end: "2026-10-16T01:00" };
const trip = { uid: "a", calendar: "trips", allDay: true, start: "2026-10-14", end: "2026-10-16" };
assert.strictEqual(C.lastDay(timed), "2026-10-15");
assert.strictEqual(C.lastDay(night), "2026-10-15");   // ends at midnight: not on the 16th
assert.strictEqual(C.lastDay(late), "2026-10-16");
assert.strictEqual(C.lastDay(trip), "2026-10-16");
assert.ok(C.onDay(trip, "2026-10-15") && !C.onDay(trip, "2026-10-17"));

// The day's agenda: all-day first, then by start.
const evs = [timed, late, trip, night];
assert.deepStrictEqual(C.dayEvents(evs, "2026-10-15").map(e => e.uid), ["a", "t", "n", "l"]);
assert.deepStrictEqual(C.dayEvents(evs, "2026-10-16").map(e => e.uid), ["a", "l"]);

// Dots: one per calendar with something that day, in calendar order.
const cals = [{ name: "home", color: "green" }, { name: "trips", color: "#ff8800" }];
assert.deepStrictEqual(C.dots(evs, "2026-10-15", cals).map(c => c.name), ["home", "trips"]);
assert.deepStrictEqual(C.dots(evs, "2026-10-14", cals).map(c => c.name), ["trips"]);
assert.deepStrictEqual(C.dots(evs, "2026-10-20", cals), []);

assert.strictEqual(C.timeLabel(trip, "2026-10-15"), "all day");
assert.strictEqual(C.timeLabel(timed, "2026-10-15"), "14:30–15:30");
assert.strictEqual(C.timeLabel(late, "2026-10-15"), "23:00–…");
assert.strictEqual(C.timeLabel(late, "2026-10-16"), "…–01:00");

// Times.
assert.ok(C.validTime("09:05") && C.validTime("23:59"));
assert.ok(!C.validTime("24:00") && !C.validTime("9:05") && !C.validTime("ab:cd"));
assert.strictEqual(C.stepTime("14:30", 15), "14:45");
assert.strictEqual(C.stepTime("23:50", 15), "00:05");
assert.strictEqual(C.stepTime("00:05", -15), "23:50");
assert.strictEqual(C.stepTime("bad", 15), "bad");

// A new event: the next whole hour, one hour long, alarm 1h.
const d = C.newDraft("2026-10-20", new Date(2026, 9, 13, 9, 20), "home");
assert.strictEqual(d.startTime, "10:00");
assert.strictEqual(d.endTime, "11:00");
assert.strictEqual(d.startDay, "2026-10-20");
assert.strictEqual(d.alarm, "1h");
assert.strictEqual(d.calendar, "home");
assert.strictEqual(d.source, "");
assert.strictEqual(C.newDraft("2026-10-20", new Date(2026, 9, 13, 23, 10), "home").startTime, "22:00");

// From calctl's JSON.
const fe = C.fromEvent({ uid: "u", calendar: "home", title: "Trip", allDay: true, start: "2026-10-14",
    end: "2026-10-16", repeat: "none", until: "", alarm: "none", location: "", notes: "", override: false });
assert.strictEqual(fe.startDay, "2026-10-14");
assert.strictEqual(fe.endDay, "2026-10-16");
assert.strictEqual(fe.allDay, true);
assert.strictEqual(fe.source, "home");             // where it lives now, for edit and delete
assert.strictEqual(C.fromEvent(Object.assign({}, timed, { title: "x" })).startTime, "14:30");

// Picking on the grid: a click moves the event and keeps its length in days;
// shift-click sets the end, or the start when before it.
const p1 = C.pick(fe, "2026-10-20", false);
assert.strictEqual(p1.startDay, "2026-10-20");
assert.strictEqual(p1.endDay, "2026-10-22");
assert.strictEqual(fe.startDay, "2026-10-14");          // input untouched
const p2 = C.pick(fe, "2026-10-18", true);
assert.strictEqual(p2.startDay, "2026-10-14");
assert.strictEqual(p2.endDay, "2026-10-18");
const p3 = C.pick(fe, "2026-10-10", true);
assert.strictEqual(p3.startDay, "2026-10-10");
assert.strictEqual(p3.endDay, "2026-10-16");

// To calctl's JSON.
assert.strictEqual(C.toPayload(Object.assign({}, d, { title: " " })).error, "Title is empty");
assert.strictEqual(C.toPayload(Object.assign({}, d, { title: "x", startTime: "9:00" })).error, "Times are HH:MM");
assert.strictEqual(C.toPayload(Object.assign({}, d, { title: "x", endTime: "09:00" })).error, "End is before start");
assert.strictEqual(C.toPayload(Object.assign({}, d, { title: "x", repeat: "weekly", until: "2026-10-01" })).error,
    "Until is before the start");
const ok = C.toPayload(Object.assign({}, d, { title: " Gym ", repeat: "weekly", until: "2026-12-01" })).payload;
assert.deepStrictEqual(ok, { calendar: "home", title: "Gym", allDay: false, start: "2026-10-20T10:00",
    end: "2026-10-20T11:00", repeat: "weekly", until: "2026-12-01", alarm: "1h", location: "", notes: "" });
const ad = C.toPayload(Object.assign({}, fe, { repeat: "none", until: "2026-12-01" })).payload;
assert.strictEqual(ad.start, "2026-10-14");
assert.strictEqual(ad.end, "2026-10-16");
assert.strictEqual(ad.until, "");                        // no repeat, no until

console.log("test_cal: ok");