# Implementation Plan: Testbed Library Pages + Playwright E2E Suite **Date:** 2026-04-24 **Branch:** genesis **App path:** `apps/testbed/` **Depends on:** `2026-04-24-testbed-foundation.md` (app shell, layout components, testing-e2e modules must exist first) --- ## Goal Implement all 10 library pages for `apps/testbed/` and the complete Playwright E2E test suite. Every page drives real `@suaveplan/*` library code — no mocks, no hardcoded responses. Every spec enforces zero console errors, zero network failures, and locates elements exclusively by `data-testid`. --- ## Architecture ```text apps/testbed/ src/client/pages/ ← 10 page directories, one per library home/ ← grid of all library cards crypto/ ← XChaCha20-Poly1305 encrypt/decrypt round-trip logger/ ← ConsoleLogger + EventLogger demo datetime/ ← business-day arithmetic i18n/ ← message catalog + pluralization + locale switcher feature-flags/ ← FlagManager with Percentage + UserTarget strategies storage-s3/ ← upload/list/download via /api/storage proxy surrealdb/ ← create/query/delete record via /api/database proxy network-webrtc/ ← WebRTC peer connection (?role=host|peer&session=) extension-test/ ← login form + injection target for extension E2E e2e/ layout/ ← navigation, theme, locale, console-baseline, network-baseline libraries/ ← one spec per library page multi-session/ ← webrtc-mesh, server-failover, websocket-gateway, broadcast-channel extensions/ ← content-script, autofill-detection ``` **Standard page contract** (every page, without exception): - `

` — package name - `
` — wraps each interactive demo - `
` — live result display inside demo card - `
` — console capture panel below demo --- ## Tech Stack - **React 19** with hooks — no class components - **Tailwind v4** — utility classes, `data-theme` dark mode - **`@suaveplan/crypto`**, **`@suaveplan/logger`**, **`@suaveplan/datetime`**, **`@suaveplan/i18n`**, **`@suaveplan/feature-flags`** — real library imports - **Hono** API proxy — `/api/storage`, `/api/database`, `/api/test-control` - **`@suaveplan/testing-e2e`** — `attachConsoleMonitor`, `attachNetworkMonitor`, `createMultiSession`, `launchWithExtension` - **Playwright** — `chromium`, `firefox`, `webkit`, `mobile-chrome`, `mobile-safari`, `chromium-extensions` > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. --- ## Pre-flight Checklist Before writing any code: 1. Confirm the app shell exists: `ls /mnt/s/web/genesis/apps/testbed/src/client/` — must contain `main.tsx`, `app.tsx`, `routes.ts`, `components/`. 2. Confirm layout components exist: `ls /mnt/s/web/genesis/apps/testbed/src/client/components/layout/` — must contain `sidebar.tsx`, `header.tsx`, `console-panel.tsx`, `locale-switcher.tsx`, `theme-toggle.tsx`. 3. Confirm shared components exist: `ls /mnt/s/web/genesis/apps/testbed/src/client/components/shared/` — must contain `demo-card.tsx`, `code-block.tsx`, `status-badge.tsx`. 4. Confirm testing-e2e has new modules: `ls /mnt/s/web/genesis/universal/dev/testing-e2e/src/` — must include `console-monitor/`, `network-monitor/`, `multi-session/`, `extension-fixture/`. 5. Confirm `@suaveplan/crypto`, `@suaveplan/logger`, `@suaveplan/datetime`, `@suaveplan/i18n`, `@suaveplan/feature-flags` are in `apps/testbed/package.json` dependencies. 6. Confirm `routes.ts` exists and exports the `RouteEntry[]` array with all 10 routes. 7. Confirm the Hono server has `/api/storage`, `/api/database`, and `/api/test-control` route files. If any of the above are missing, stop and complete `2026-04-24-testbed-foundation.md` first. --- ## Chunk 1 — Home Page + Crypto Page + Logger Page + Datetime Page **Goal:** Four pages wired to real library code. Commit after this chunk. ### Task 1.1 — Home page **File:** `apps/testbed/src/client/pages/home/home-page.tsx` The home page renders a responsive grid of library cards. It imports the `routes` array from `../../routes.js` and maps over it, rendering one card per entry. Each card links to the library's route. Required elements: - `data-testid="page-title"` on `

Testbed

` (the home page title is "Testbed", not a package name) - `data-testid="library-card"` on each card ```tsx import { routes } from "../../routes.js"; import { Link } from "react-router-dom"; export function HomePage() { return (

Testbed

Live demonstrations of every @suaveplan library against real services.

{routes.map((route) => (

{route.category}

{route.label}

))}
); } ``` **File:** `apps/testbed/src/client/pages/home/home-page.md` Required sections: Purpose (home page renders route manifest as card grid), Features (dynamic card grid, category grouping, links), Basic Example (import and use), Advanced Example (adding a new library card via routes.ts), API Reference (no props — uses routes manifest), Implementation Notes (reads from routes.ts, no hardcoded list). Minimum 200 words excluding code blocks. **File:** `apps/testbed/src/client/pages/home/home-page.stories.tsx` ```tsx import type { Meta, StoryObj } from "@storybook/react"; import { MemoryRouter } from "react-router-dom"; import { HomePage } from "./home-page.js"; const meta: Meta = { title: "Pages/Home", component: HomePage, decorators: [(Story) => ], }; export default meta; export const Default: StoryObj = {}; ``` **File:** `apps/testbed/src/client/pages/home/index.ts` ```ts export { HomePage } from "./home-page.js"; ``` --- ### Task 1.2 — Crypto page **File:** `apps/testbed/src/client/pages/crypto/crypto-page.tsx` Demonstrates XChaCha20-Poly1305 encrypt/decrypt round-trip from `@suaveplan/crypto`. On mount, generate a random key. When the user types plaintext and clicks "Encrypt", call `encrypt()`, display the ciphertext hex. Click "Decrypt" to call `decrypt()` and show the recovered plaintext. Use `useState` for: `plaintext`, `ciphertext`, `decrypted`, `error`. Use `useEffect` on mount to call `crypto.generateKey()` and store the key in a `useRef`. ```tsx import { useState, useRef, useEffect } from "react"; import { createCrypto } from "@suaveplan/crypto"; const crypto = createCrypto(); export function CryptoPage() { const keyRef = useRef(null); const [plaintext, setPlaintext] = useState("Hello, Suaveplan!"); const [ciphertext, setCiphertext] = useState(""); const [decrypted, setDecrypted] = useState(""); const [error, setError] = useState(""); useEffect(() => { crypto.generateKey().then((k) => { keyRef.current = k; }); }, []); async function handleEncrypt() { setError(""); try { const key = keyRef.current!; const nonce = crypto.randomBytes(24); const ct = await crypto.encrypt({ key, nonce, plaintext: new TextEncoder().encode(plaintext) }); setCiphertext(Buffer.from(ct).toString("hex")); } catch (e) { setError(String(e)); } } async function handleDecrypt() { setError(""); try { // For demo: re-encrypt to get a fresh round-trip const key = keyRef.current!; const nonce = crypto.randomBytes(24); const ct = await crypto.encrypt({ key, nonce, plaintext: new TextEncoder().encode(plaintext) }); const pt = await crypto.decrypt({ key, nonce, ciphertext: ct }); setDecrypted(new TextDecoder().decode(pt)); } catch (e) { setError(String(e)); } } return (

@suaveplan/crypto

XChaCha20-Poly1305 Round-Trip