85 lines
3.9 KiB
TypeScript
85 lines
3.9 KiB
TypeScript
import { create, type MessageInitShape } from "@bufbuild/protobuf";
|
|
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
|
|
import { describe, expect, it } from "vitest";
|
|
import { buildCalendarMonthModel } from "@/components/CalendarView/dayModel";
|
|
import { AttachmentSchema } from "@/types/proto/api/v1/attachment_service_pb";
|
|
import { type Memo, MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
|
|
|
|
const memoAt = (date: Date, overrides: MessageInitShape<typeof MemoSchema> = {}): Memo =>
|
|
create(MemoSchema, {
|
|
name: `memos/${date.getTime()}`,
|
|
createTime: timestampFromDate(date),
|
|
updateTime: timestampFromDate(new Date(date.getTime() + 60_000)),
|
|
content: "",
|
|
...overrides,
|
|
});
|
|
|
|
const image = (name: string) => create(AttachmentSchema, { name: `attachments/${name}`, filename: `${name}.jpg`, type: "image/jpeg" });
|
|
const pdf = create(AttachmentSchema, { name: "attachments/doc", filename: "doc.pdf", type: "application/pdf" });
|
|
|
|
describe("calendar month model", () => {
|
|
it("groups memos by local day and counts them", () => {
|
|
const model = buildCalendarMonthModel(
|
|
[memoAt(new Date(2026, 7, 2, 9)), memoAt(new Date(2026, 7, 2, 21)), memoAt(new Date(2026, 7, 3, 1))],
|
|
"create_time",
|
|
);
|
|
expect(Object.keys(model).sort()).toEqual(["2026-08-02", "2026-08-03"]);
|
|
expect(model["2026-08-02"].memos).toHaveLength(2);
|
|
expect(model["2026-08-03"].memos).toHaveLength(1);
|
|
});
|
|
|
|
it("lists memos as rows in time order with a thumbnail when they carry an image", () => {
|
|
const model = buildCalendarMonthModel(
|
|
[
|
|
memoAt(new Date(2026, 7, 2, 21), { snippet: "late", attachments: [image("late")] }),
|
|
memoAt(new Date(2026, 7, 2, 9), { snippet: "early", attachments: [pdf] }),
|
|
],
|
|
"create_time",
|
|
);
|
|
const entries = model["2026-08-02"].entries;
|
|
expect(entries.map((entry) => entry.text)).toEqual(["early", "late"]);
|
|
expect(entries[0].thumbnailUrl).toBeUndefined();
|
|
expect(entries[1].thumbnailUrl).toContain("attachments/late/late.jpg");
|
|
expect(entries[1].thumbnailUrl).toContain("thumbnail=true");
|
|
});
|
|
|
|
it("uses first non-empty lines, keeps image-only memos, and drops blank ones", () => {
|
|
const model = buildCalendarMonthModel(
|
|
[
|
|
memoAt(new Date(2026, 7, 2, 8), { snippet: " \nSecond line first\nmore" }),
|
|
memoAt(new Date(2026, 7, 2, 9), { snippet: " ", content: "Raw content only" }),
|
|
memoAt(new Date(2026, 7, 2, 10), { content: " " }),
|
|
memoAt(new Date(2026, 7, 2, 11), { content: "", attachments: [image("photo")] }),
|
|
],
|
|
"create_time",
|
|
);
|
|
expect(model["2026-08-02"].memos).toHaveLength(4);
|
|
expect(model["2026-08-02"].entries.map((entry) => entry.text)).toEqual(["Second line first", "Raw content only", ""]);
|
|
expect(model["2026-08-02"].entries[2].thumbnailUrl).toBeDefined();
|
|
});
|
|
|
|
it("caps entries while still counting every memo", () => {
|
|
const memos = Array.from({ length: 10 }, (_, index) => memoAt(new Date(2026, 7, 2, index + 1), { snippet: `m${index}` }));
|
|
const model = buildCalendarMonthModel(memos, "create_time");
|
|
expect(model["2026-08-02"].memos).toHaveLength(10);
|
|
expect(model["2026-08-02"].entries).toHaveLength(8);
|
|
});
|
|
|
|
it("buckets by update time when that is the basis", () => {
|
|
const model = buildCalendarMonthModel([memoAt(new Date(2026, 7, 31, 23, 59, 30))], "update_time");
|
|
expect(Object.keys(model)).toEqual(["2026-09-01"]);
|
|
});
|
|
|
|
it("counts redacted memos without giving them a row", () => {
|
|
const model = buildCalendarMonthModel(
|
|
[
|
|
memoAt(new Date(2026, 7, 2, 9), { tags: ["private"], snippet: "secret", attachments: [image("secret")] }),
|
|
memoAt(new Date(2026, 7, 2, 10), { snippet: "public" }),
|
|
],
|
|
"create_time",
|
|
{ isRedacted: (memo) => memo.tags.includes("private") },
|
|
);
|
|
expect(model["2026-08-02"].memos).toHaveLength(2);
|
|
expect(model["2026-08-02"].entries.map((entry) => entry.text)).toEqual(["public"]);
|
|
});
|
|
});
|