Files
2026-07-13 13:00:08 +08:00

173 lines
5.8 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import { performance } from "node:perf_hooks";
import { fileURLToPath } from "node:url";
import ts from "typescript";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const sourcePath = path.join(root, "src", "lib", "todoVisibility.ts");
const source = readFileSync(sourcePath, "utf8");
const transpiled = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.ES2022,
target: ts.ScriptTarget.ES2022,
},
}).outputText;
const moduleUrl = `data:text/javascript;base64,${Buffer.from(transpiled).toString("base64")}`;
const {
dismissedTodoKeyForScope,
scopedTodoBatchKey,
scopedTodoDismissalKey,
shouldOpenTodoPanelByDefault,
shouldShowTodoPanel,
todoBatchKey,
todoDismissalKey,
todoPanelScope,
} = await import(moduleUrl);
const completedTodos = [
{ content: "Inspect the report", status: "completed" },
{ content: "Ship the fix", status: "completed" },
];
const activeTodos = [
{ content: "Inspect the report", status: "in_progress" },
{ content: "Ship the fix", status: "pending" },
];
assert.equal(
shouldShowTodoPanel("todo-final", null, completedTodos),
true,
"a completed todo list stays visible in collapsed form until the user dismisses it",
);
assert.equal(
shouldShowTodoPanel("todo-active", null, [{ content: "Run tests", status: "in_progress" }]),
true,
"an active todo_write remains visible",
);
assert.equal(
shouldShowTodoPanel("todo-final", "todo-final", completedTodos),
false,
"a user dismissal still hides that exact todo list",
);
const completedKey = todoDismissalKey(completedTodos);
const dismissedBySession = new Set([scopedTodoDismissalKey("session:a", completedKey)]);
assert.equal(
shouldShowTodoPanel(completedKey, dismissedTodoKeyForScope("session:a", dismissedBySession, completedKey), completedTodos),
false,
"a completed todo dismissal hides the list in the session where it was closed",
);
assert.equal(
shouldShowTodoPanel(completedKey, dismissedTodoKeyForScope("session:b", dismissedBySession, completedKey), completedTodos),
true,
"a completed todo dismissal in one session does not hide another session's list",
);
dismissedBySession.add(scopedTodoDismissalKey("session:b", completedKey));
assert.equal(
shouldShowTodoPanel(completedKey, dismissedTodoKeyForScope("session:a", dismissedBySession, completedKey), completedTodos),
false,
"closing another session's todo does not forget the first session dismissal",
);
assert.equal(
todoPanelScope({
activeTabId: "tab-a",
activeTab: {
id: "tab-a",
scope: "project",
workspaceRoot: "/repo",
topicId: "topic-a",
sessionPath: "/sessions/a.jsonl",
},
}),
"session:/sessions/a.jsonl",
"a restored history session uses its session path as the todo panel scope",
);
assert.equal(
todoPanelScope({
activeTabId: "tab-b",
activeTab: {
id: "tab-a",
scope: "project",
workspaceRoot: "/repo",
topicId: "topic-a",
sessionPath: "/sessions/a.jsonl",
},
eventChannel: "desktop-events",
}),
"tab:tab-b",
"a stale active-tab fallback must not scope dismissal to the previous session",
);
assert.equal(
todoPanelScope({
activeTabId: "tab-c",
activeTab: {
id: "tab-c",
scope: "project",
workspaceRoot: "/repo",
topicId: "topic-a",
},
}),
"tab:tab-c",
"an unsaved topic session uses its tab id so sibling sessions do not share todo state",
);
assert.equal(shouldShowTodoPanel(null, null, completedTodos), false, "no canonical todo item means no panel");
assert.equal(shouldShowTodoPanel("todo-empty", null, []), false, "empty todo lists do not render a panel");
const activeKey = todoDismissalKey(activeTodos);
assert.equal(
activeKey,
todoDismissalKey(activeTodos.map((todo) => ({ ...todo }))),
"the same task list keeps a stable dismissal key across restored event ids",
);
assert.equal(
shouldShowTodoPanel(activeKey, activeKey, activeTodos),
true,
"an incomplete restored todo list must reappear even after a stale local dismissal",
);
assert.notEqual(
activeKey,
todoDismissalKey([{ ...activeTodos[0], status: "completed" }, { ...activeTodos[1], status: "in_progress" }]),
"real progress produces a fresh dismissal key",
);
assert.equal(
todoBatchKey(activeTodos),
todoBatchKey([{ ...activeTodos[0], status: "completed" }, { ...activeTodos[1], status: "in_progress" }]),
"status progress stays in the same todo batch",
);
assert.equal(
scopedTodoBatchKey("session:a", todoBatchKey(activeTodos)),
scopedTodoBatchKey("session:a", todoBatchKey([{ ...activeTodos[0], status: "completed" }, { ...activeTodos[1], status: "in_progress" }])),
"status progress keeps the same scoped open-state key",
);
assert.notEqual(
scopedTodoBatchKey("session:a", todoBatchKey(activeTodos)),
scopedTodoBatchKey("session:b", todoBatchKey(activeTodos)),
"the same task list in a different session gets isolated open state",
);
assert.notEqual(
todoBatchKey(activeTodos),
todoBatchKey([{ content: "Run a different task", status: "in_progress" }]),
"new task content creates a new todo batch",
);
assert.equal(
shouldOpenTodoPanelByDefault(),
false,
"todo batches collapse by default regardless of completion",
);
const iterations = 200_000;
const started = performance.now();
for (let i = 0; i < iterations; i += 1) {
if (shouldShowTodoPanel("todo-perf", "todo-perf", completedTodos)) {
throw new Error("unexpected visible todo panel during performance loop");
}
}
const elapsed = performance.now() - started;
const perCallUs = (elapsed * 1000) / iterations;
assert.ok(elapsed < 500, `todo visibility check is too slow: ${elapsed.toFixed(2)} ms`);
console.log(
`todo visibility checks: ${iterations} calls in ${elapsed.toFixed(2)} ms (${perCallUs.toFixed(3)} us/call)`,
);