mirror of
https://github.com/zadam/trilium.git
synced 2026-03-01 18:13:39 +01:00
Add recurrence testing, use dayjs for calendar
This commit is contained in:
parent
8554dc249c
commit
e029379194
@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { buildNote, buildNotes } from "../../../test/easy-froca.js";
|
||||
import { buildEvent, buildEvents } from "./event_builder.js";
|
||||
import { LOCALE_MAPPINGS } from "./index.js";
|
||||
@ -148,7 +148,7 @@ describe("Promoted attributes", () => {
|
||||
expect(event).toHaveLength(1);
|
||||
expect(event[0]?.promotedAttributes).toMatchObject([
|
||||
[ "assignee", "Target note" ]
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it("supports start time and end time", async () => {
|
||||
@ -177,6 +177,86 @@ describe("Promoted attributes", () => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe("Recurrence", () => {
|
||||
it("supports valid recurrence without end date", async () => {
|
||||
const noteIds = buildNotes([
|
||||
{
|
||||
title: "Recurring Event",
|
||||
"#startDate": "2025-05-05",
|
||||
"#recurrence": "FREQ=DAILY;COUNT=5"
|
||||
}
|
||||
]);
|
||||
const events = await buildEvents(noteIds);
|
||||
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0]).toMatchObject({
|
||||
title: "Recurring Event",
|
||||
start: "2025-05-05",
|
||||
});
|
||||
expect(events[0].rrule).toContain("DTSTART:20250505");
|
||||
expect(events[0].rrule).toContain("FREQ=DAILY;COUNT=5");
|
||||
expect(events[0].end).toBeUndefined();
|
||||
});
|
||||
|
||||
it("supports recurrence with start and end time (duration calculated)", async () => {
|
||||
const noteIds = buildNotes([
|
||||
{
|
||||
title: "Timed Recurring Event",
|
||||
"#startDate": "2025-05-05",
|
||||
"#startTime": "13:00",
|
||||
"#endTime": "15:30",
|
||||
"#recurrence": "FREQ=WEEKLY;COUNT=3"
|
||||
}
|
||||
]);
|
||||
const events = await buildEvents(noteIds);
|
||||
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0]).toMatchObject({
|
||||
title: "Timed Recurring Event",
|
||||
start: "2025-05-05T13:00:00",
|
||||
duration: "02:30"
|
||||
});
|
||||
expect(events[0].rrule).toContain("DTSTART:20250505T130000");
|
||||
expect(events[0].end).toBeUndefined();
|
||||
});
|
||||
|
||||
it("removes end date when recurrence is valid", async () => {
|
||||
const noteIds = buildNotes([
|
||||
{
|
||||
title: "Recurring With End",
|
||||
"#startDate": "2025-05-05",
|
||||
"#endDate": "2025-05-07",
|
||||
"#recurrence": "FREQ=DAILY;COUNT=2"
|
||||
}
|
||||
]);
|
||||
const events = await buildEvents(noteIds);
|
||||
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].rrule).toBeDefined();
|
||||
expect(events[0].end).toBeUndefined();
|
||||
});
|
||||
|
||||
it("writes to console on invalid recurrence rule", async () => {
|
||||
const noteIds = buildNotes([
|
||||
{
|
||||
title: "Invalid Recurrence",
|
||||
"#startDate": "2025-05-05",
|
||||
"#recurrence": "RRULE:FREQ=INVALID"
|
||||
}
|
||||
]);
|
||||
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
await buildEvents(noteIds);
|
||||
const calledWithInvalid = consoleSpy.mock.calls.some(call =>
|
||||
call[0].includes("has an invalid #recurrence string")
|
||||
);
|
||||
expect(calledWithInvalid).toBe(true);
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("Building locales", () => {
|
||||
it("every language has a locale defined", async () => {
|
||||
for (const { id, contentOnly } of LOCALES) {
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { EventInput, EventSourceFuncArg, EventSourceInput } from "@fullcalendar/core/index.js";
|
||||
import { dayjs } from "@triliumnext/commons";
|
||||
import clsx from "clsx";
|
||||
import { start } from "repl";
|
||||
import * as rruleLib from 'rrule';
|
||||
|
||||
import FNote from "../../../entities/fnote";
|
||||
import froca from "../../../services/froca";
|
||||
import server from "../../../services/server";
|
||||
import toastService from "../../../services/toast";
|
||||
import { formatDateToLocalISO, getCustomisableLabel, getMonthsInDateRange, offsetDate } from "./utils";
|
||||
import { getCustomisableLabel, getMonthsInDateRange } from "./utils";
|
||||
|
||||
interface Event {
|
||||
startDate: string,
|
||||
@ -127,9 +129,10 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
|
||||
|
||||
startDate = (startTime ? `${startDate}T${startTime}:00` : startDate);
|
||||
if (!startTime) {
|
||||
const endDateOffset = offsetDate(endDate ?? startDate, 1);
|
||||
if (endDateOffset) {
|
||||
endDate = formatDateToLocalISO(endDateOffset);
|
||||
if (endDate) {
|
||||
endDate = dayjs(endDate).add(1, "day").format("YYYY-MM-DD");
|
||||
} else if (startDate) {
|
||||
endDate = dayjs(startDate).add(1, "day").format("YYYY-MM-DD");
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +153,7 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
|
||||
|
||||
if (recurrence) {
|
||||
// Generate rrule string
|
||||
const rruleString = `DTSTART:${startDate.replace(/[-:]/g, "")}\n${recurrence}`;
|
||||
const rruleString = `DTSTART:${dayjs(startDate).format("YYYYMMDD[T]HHmmss")}\n${recurrence}`;
|
||||
|
||||
// Validate rrule string
|
||||
let rruleValid = true;
|
||||
@ -164,13 +167,11 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
|
||||
delete eventData.end;
|
||||
eventData.rrule = rruleString;
|
||||
if (endDate){
|
||||
const diffMs = new Date(endDate).getTime() - new Date(startDate).getTime();
|
||||
const hours = Math.floor(diffMs / 3600000);
|
||||
const minutes = Math.floor((diffMs / 60000) % 60);
|
||||
eventData.duration = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
||||
const duration = dayjs.duration(dayjs(endDate).diff(dayjs(startDate)));
|
||||
eventData.duration = duration.format("HH:mm");
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Note "${note.noteId} ${note.title}" has an invalid #recurrence string. Excluding...`);
|
||||
throw new Error(`Note "${note.noteId} ${note.title}" has an invalid #recurrence string ${recurrence}. Excluding...`);
|
||||
}
|
||||
}
|
||||
events.push(eventData);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user