Files
2026-08-08 16:30:22 +02:00

107 lines
3.7 KiB
TypeScript

import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { parseBoard, renderBoard } from "../src/model/board";
import { serializeTaskLine } from "../src/model/serialize";
const FIXTURES = join(import.meta.dirname, "fixtures");
const BOARDS = readdirSync(FIXTURES).filter((name) => name.endsWith(".md"));
describe("board round-trip", () => {
it("has boards to work with", () => {
expect(BOARDS.length).toBeGreaterThan(0);
});
for (const name of BOARDS) {
const content = readFileSync(join(FIXTURES, name), "utf8");
const board = parseBoard(`Boards/${name}`, content);
it(`${name}: re-renders byte for byte`, () => {
expect(renderBoard(board)).toBe(content);
});
it(`${name}: every task line survives parse then serialize`, () => {
for (const task of board.tasks) {
expect(serializeTaskLine(task), `line ${task.line + 1}`).toBe(task.raw);
}
});
it(`${name}: finds tasks and lanes`, () => {
expect(board.lanes.length).toBeGreaterThan(0);
expect(board.tasks.length).toBeGreaterThan(0);
for (const task of board.tasks) {
expect(board.lines[task.line]).toBe(task.raw);
}
});
}
});
describe("field extraction", () => {
const parse = (line: string) => {
const board = parseBoard("x.md", `## lane\n\n${line}\n`);
return board.tasks[0];
};
it("reads recurrence followed by dates", () => {
const task = parse("- [ ] water the plants 🔁 every day 📅 2026-08-08");
expect(task.text).toBe("water the plants");
expect(task.recurrence).toBe("every day");
expect(task.due).toBe("2026-08-08");
expect(task.scheduled).toBeNull();
});
it("reads scheduled, due and done together", () => {
const task = parse(
"- [x] pay the electricity bill ⏳ 2026-07-28 📅 2026-07-29 ✅ 2026-07-28",
);
expect(task.status).toBe("x");
expect(task.text).toBe("pay the electricity bill");
expect(task.scheduled).toBe("2026-07-28");
expect(task.due).toBe("2026-07-29");
expect(task.done).toBe("2026-07-28");
});
it("keeps wikilinks and embeds inside the description", () => {
const task = parse("- [ ] call [[Alex Rivera|Alex]] about the move 📅 2026-08-09");
expect(task.text).toBe("call [[Alex Rivera|Alex]] about the move");
expect(task.due).toBe("2026-08-09");
});
it("does not mistake a url fragment for a tag", () => {
const task = parse("- [ ] compare tiers https://example.com/pricing#plans");
expect(task.tags).toEqual([]);
expect(task.text).toBe("compare tiers https://example.com/pricing#plans");
});
it("reads priority and writes it back in Tasks' order", () => {
const task = parse("- [ ] fix the mobile nav ⏫ 📅 2026-08-10");
expect(task.priority).toBe(4);
expect(serializeTaskLine(task)).toBe("- [ ] fix the mobile nav ⏫ 📅 2026-08-10");
});
it("captures a multi-line kanban card body", () => {
const board = parseBoard(
"x.md",
"## lane\n\n- [x] rewrite the copy ✅ 2026-08-01\n\t- shorter intro\n\t- link the docs\n",
);
expect(board.tasks[0].body).toEqual(["\t- shorter intro", "\t- link the docs"]);
});
it("marks archive lanes", () => {
const board = parseBoard(
"x.md",
"## live\n\n- [ ] a\n\n***\n\n## Archive\n\n- [x] b ✅ 2026-01-01\n",
);
expect(board.tasks[0].inArchive).toBe(false);
expect(board.tasks[1].inArchive).toBe(true);
});
it("ignores anything inside the kanban settings block", () => {
const board = parseBoard(
"x.md",
'## lane\n\n- [ ] real\n\n%% kanban:settings\n```\n{"kanban-plugin":"board"}\n```\n%%\n',
);
expect(board.tasks).toHaveLength(1);
});
});