Files
Snow LeeandClaude Fable 5 2db399a766 feat(webapp): [phase 2] file browsing — tree, listings, files, upload, share, palette
- FileTree with fold state, lone-root auto-open, reveal-on-deep-link;
  folder listings with heat dots and the journals-backed change feed
- FileView: markdown (HTML transformed BEFORE render, link clicks
  delegated — React re-applies dangerouslySetInnerHTML markup on
  unrelated updates, so post-commit DOM patching loses handlers),
  images, text, download card
- breadcrumbs, per-route scroll restoration (location.key memo)
- topbar actions: share dialog (mint/copy/open/revoke), history/upload/
  download buttons, ⋯ overflow menu; upload via upload/init direct or
  relay path, then tree refresh + open
- ⌘K command palette: fuzzy files/projects/actions with stemming
- e2e: 11 new browse specs (22 total green in ~12s); helpers cache one
  session cookie per identity to stay under the 10/min auth rate limit

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
2026-07-13 10:31:01 -07:00

36 lines
1.3 KiB
TypeScript

import { Page } from "@playwright/test";
export const ADMIN = "e2e@example.com";
export const MEMBER = "member@example.com";
export const PASSWORD = "e2e-pass-1";
// One real form login per identity per run, then the session cookie is
// reused — the server rate-limits credential POSTs to 10/min per IP, which
// a fresh login in every spec would trip.
type Cookies = Awaited<ReturnType<ReturnType<Page["context"]>["cookies"]>>;
const sessions = new Map<string, Cookies>();
// Signs in (through the server-rendered /auth pages on first use) and waits
// for the SPA shell to render.
export async function login(page: Page, email: string = ADMIN) {
const cached = sessions.get(email);
if (cached) {
await page.context().addCookies(cached);
await page.goto("/");
await page.waitForSelector("#sidebar");
return;
}
await page.goto("/");
await page.waitForURL(/auth\/login/);
await page.fill('input[name="email"]', email);
await page.fill('input[name="password"]', PASSWORD);
await page.click("form button");
await page.waitForSelector("#sidebar");
sessions.set(email, await page.context().cookies());
}
export async function wikiId(page: Page): Promise<string> {
const out = await (await page.request.get("/api/projects")).json();
return out.projects.find((p: { name: string }) => p.name === "wiki").id;
}