Menu
Public repository / 8b66e18ce8cb51ca3e4124f89fd1429a1d4792f3
AkurAI-Mail / src/framework/list-island.test.js
Latest change 3af23b0e937520000e1aa43c7e2eb9de2675abd1 - OLI-248: webmail list and reader as framework islands on a signals store by Ólafur Búi Ólafsson
import { describe, expect, test } from "bun:test";
import { folderIcon, listWindow, senderLabel, shortDate, threadHeight, ROW_HEIGHT, CHILD_ROW_HEIGHT } from "./list-island.js";
const threads = Array.from({ length: 40 }, (_, i) => ({ key: `t${i}`, messages: i === 5 ? [1, 2, 3] : [1] }));
describe("list window", () => {
test("draws only the rows near the viewport with honest padding", () => {
const top = listWindow(threads, {}, 0, 600);
expect(top.threads.map((t) => t.key)).toEqual(threads.slice(0, 12).map((t) => t.key));
expect(top.padTop).toBe(0);
expect(top.padBottom).toBe((40 - 12) * ROW_HEIGHT);
const mid = listWindow(threads, {}, 20 * ROW_HEIGHT, 600);
expect(mid.threads[0]?.key).toBe("t14");
expect(mid.padTop).toBe(14 * ROW_HEIGHT);
});
test("expanded threads grow by their replies", () => {
expect(threadHeight(threads[5], {})).toBe(ROW_HEIGHT);
expect(threadHeight(threads[5], { t5: true })).toBe(ROW_HEIGHT + 2 * CHILD_ROW_HEIGHT);
const open = listWindow(threads, { t5: true }, 0, 600);
expect(open.padBottom).toBe(40 * ROW_HEIGHT + 2 * CHILD_ROW_HEIGHT - (open.padTop + open.threads.reduce((sum, t) => sum + threadHeight(t, { t5: true }), 0)));
});
test("an empty list has no rows and no padding", () => {
expect(listWindow([], {}, 0, 600)).toEqual({ threads: [], padTop: 0, padBottom: 0 });
});
});
describe("row labels", () => {
test("folder icons fall back to a document", () => {
expect(folderIcon("Inbox")).toBe("Inbox");
expect(folderIcon("Trash")).toBe("Trash2");
expect(folderIcon("Projects")).toBe("FileText");
});
test("sender label prefers from, then to", () => {
expect(senderLabel({ from: "a@b.is" })).toBe("a@b.is");
expect(senderLabel({ to: "c@d.is" })).toBe("c@d.is");
expect(senderLabel({})).toBe("(unknown)");
});
test("short date shows the time today and the day otherwise", () => {
const today = new Date("2026-10-03T12:00:00");
expect(shortDate("2026-10-03T09:05:00", today)).toMatch(/09:05|9:05/);
expect(shortDate("2026-09-01T09:05:00", today)).toMatch(/Sep 1/);
expect(shortDate("not a date", today)).toBe("not a date");
});
});