Implements an e-ink daily dashboard plugin ("today") with four sections:
date/focus/success header, agenda timeline, chores checklist, and
due/overdue task lists.
Data sources:
- Focus & success text: Tana daily note (src/sources/tana.ts)
- Due/overdue tasks: Tana task search (src/sources/tana.ts)
- Agenda events: ICS calendar feeds (src/sources/ics.ts)
- Chores: Donetick API (src/sources/donetick.ts)
All sources fetch in parallel and fall back gracefully on error.
Tests use mock HTTP servers with synthetic data — no real services needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
63 lines
2.3 KiB
TypeScript
63 lines
2.3 KiB
TypeScript
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
import type { Server } from 'bun';
|
|
import { fetchDonetickChores } from './donetick';
|
|
|
|
const today = new Date().toISOString().slice(0, 10);
|
|
const tomorrow = new Date(Date.now() + 86_400_000).toISOString().slice(0, 10);
|
|
const yesterday = new Date(Date.now() - 86_400_000).toISOString().slice(0, 10);
|
|
|
|
const CHORES = [
|
|
{ id: 1, name: 'Make the bed', assignedTo: 1, nextDueDate: `${tomorrow}T00:00:00Z`, isActive: true },
|
|
{ id: 2, name: 'Water the plants', assignedTo: 1, nextDueDate: `${today}T00:00:00Z`, isActive: true },
|
|
{ id: 3, name: 'Walk the dog', assignedTo: 2, nextDueDate: `${today}T00:00:00Z`, isActive: true },
|
|
{ id: 4, name: 'Inactive chore', assignedTo: 1, nextDueDate: `${today}T00:00:00Z`, isActive: false },
|
|
{ id: 5, name: 'Shared chore', assignedTo: 2, nextDueDate: `${yesterday}T00:00:00Z`, isActive: true, assignees: [{ userId: 1 }, { userId: 2 }] },
|
|
];
|
|
|
|
let server: Server;
|
|
let baseUrl: string;
|
|
|
|
beforeAll(() => {
|
|
server = Bun.serve({
|
|
port: 0,
|
|
hostname: '127.0.0.1',
|
|
routes: {
|
|
'/eapi/v1/chore': (req) => {
|
|
if (req.headers.get('secretkey') !== 'test-token')
|
|
return new Response('Unauthorized', { status: 401 });
|
|
return Response.json(CHORES);
|
|
},
|
|
},
|
|
});
|
|
baseUrl = `http://127.0.0.1:${server.port}`;
|
|
});
|
|
|
|
afterAll(() => {
|
|
server.stop();
|
|
});
|
|
|
|
describe('donetick source', () => {
|
|
test('fetches chores for the configured user', async () => {
|
|
const chores = await fetchDonetickChores({
|
|
url: baseUrl,
|
|
token: 'test-token',
|
|
user_id: 1,
|
|
});
|
|
|
|
// Should include: Water the plants (due today), Shared chore (overdue, in assignees)
|
|
// Should exclude: Make the bed (done, nextDueDate=tomorrow), Walk the dog (different user), Inactive chore
|
|
expect(chores).toHaveLength(2);
|
|
expect(chores.map((c) => c.text)).toContain('Water the plants');
|
|
expect(chores.map((c) => c.text)).toContain('Shared chore');
|
|
expect(chores.map((c) => c.text)).not.toContain('Make the bed');
|
|
expect(chores.map((c) => c.text)).not.toContain('Walk the dog');
|
|
expect(chores.map((c) => c.text)).not.toContain('Inactive chore');
|
|
|
|
});
|
|
|
|
test('throws on auth failure', async () => {
|
|
await expect(
|
|
fetchDonetickChores({ url: baseUrl, token: 'wrong', user_id: 1 })
|
|
).rejects.toThrow('401');
|
|
});
|
|
});
|