Add today-view plugin with Tana, ICS, and Donetick data sources
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>
This commit is contained in:
parent
ddcb03d3dd
commit
2e34246d14
12 changed files with 1665 additions and 11 deletions
63
src/sources/donetick.test.ts
Normal file
63
src/sources/donetick.test.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue