The filesystem is the database: GET reads the tree through the same module the index generator now imports, PATCH splices frontmatter (status, priority, due, order) and never touches a body byte, and every write regenerates TODO.md. Columns open/doing/blocked/done, native drag between them, selection and theme filter in the URL. Registry entry 'todos' (kind admin, 22 entries, docs:check green), isSuperAdmin double-gate on the router. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { bodyOf, frontmatter, patchFrontmatter } from './todo-tree';
|
|
|
|
const FILE = `---
|
|
id: fleet/an-item
|
|
title: "An item, with: punctuation"
|
|
theme: fleet
|
|
status: open
|
|
created: 2026-08-21
|
|
order: 2
|
|
---
|
|
|
|
**The body.** Hand-written prose with --- inside it.
|
|
|
|
- a list
|
|
`;
|
|
|
|
describe('frontmatter', () => {
|
|
test('reads plain and JSON-quoted values', () => {
|
|
const fm = frontmatter(FILE);
|
|
expect(fm.id).toBe('fleet/an-item');
|
|
expect(fm.title).toBe('An item, with: punctuation');
|
|
expect(fm.order).toBe('2');
|
|
});
|
|
|
|
test('accepts prettier-style single quotes', () => {
|
|
expect(frontmatter(`---\ntitle: 'A quoted title'\n---\n`).title).toBe('A quoted title');
|
|
});
|
|
|
|
test('no frontmatter → empty', () => {
|
|
expect(frontmatter('just prose')).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe('bodyOf', () => {
|
|
test('returns everything after the fence, trimmed of leading blank lines', () => {
|
|
expect(bodyOf(FILE).startsWith('**The body.**')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('patchFrontmatter', () => {
|
|
test('replaces an existing key in place', () => {
|
|
const out = patchFrontmatter(FILE, { status: 'done' });
|
|
expect(frontmatter(out).status).toBe('done');
|
|
expect(out.match(/^status:/gm)).toHaveLength(1);
|
|
});
|
|
|
|
test('appends a key that was absent', () => {
|
|
const out = patchFrontmatter(FILE, { priority: 'urgent' });
|
|
expect(frontmatter(out).priority).toBe('urgent');
|
|
});
|
|
|
|
test('never touches the body — byte for byte', () => {
|
|
const out = patchFrontmatter(FILE, { status: 'doing', order: '9' });
|
|
expect(bodyOf(out)).toBe(bodyOf(FILE));
|
|
});
|
|
|
|
test('a --- inside the body is not mistaken for the fence', () => {
|
|
const out = patchFrontmatter(FILE, { status: 'done' });
|
|
expect(out).toContain('with --- inside it');
|
|
});
|
|
});
|