Files
2026-05-27 23:29:53 +01:00
..
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00
2026-05-27 23:29:53 +01:00

@suaveplan/testbed

A live demonstration app for every @suaveplan library, running against real services. Each page exercises the actual library API — no stubs, no mocks.

Documentation tree

  • console-panel — ConsolePanel intercepts window.console.log, warn, and error on mount and renders each captured message in an in…
  • header — Header is the top-level application bar for the testbed. It displays the app title on the left and renders `LocaleSwi…
  • locale-switcher — LocaleSwitcher renders a <select> allowing the user to switch between the four supported locales (en, fr, de,…
  • sidebar — Sidebar renders the primary navigation for the testbed as a vertically scrollable nav panel. Routes are grouped by th…
  • theme-toggle — ThemeToggle is a button that toggles between light and dark visual themes. It applies the theme by setting a `dat…
  • code-block — CodeBlock renders a preformatted code snippet with monospace styling and an optional language label. It is used on li…
  • demo-card — DemoCard is the standard wrapper for every interactive library demonstration in the testbed. It provides a consistent…
  • status-badge — StatusBadge renders a colored pill badge indicating ok, error, or pending state. It is used on library demo pag…
  • crypto — (no submodules) <!-- en…
  • datetime — (no submodules) <!-- en…
  • extension-test — (no submodules) <!-- en…
  • feature-flags — (no submodules) <!-- en…
  • home — (no submodules) <!-- en…
  • i18n — (no submodules) <!-- en…
  • logger — (no submodules) <!-- en…
  • network-broadcast — (no submodules) <!-- en…
  • network-webrtc — (no submodules) <!-- en…
  • network-websocket — (no submodules) <!-- en…
  • storage-s3 — (no submodules) <!-- en…
  • surrealdb — (no submodules) <!-- en…
- [crypto-page](./src/client/pages/crypto/crypto-page.md) — _(no submodules)_- [datetime](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_ - [datetime-page](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_- [extension-test](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_ - [extension-test-page](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_- [feature-flags](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_ - [feature-flags-page](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_- [home](./src/client/pages/home/home-page.md) — _(no submodules)_ - [home-page](./src/client/pages/home/home-page.md) — _(no submodules)_- [i18n](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_ - [i18n-page](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_- [logger](./src/client/pages/logger/logger-page.md) — _(no submodules)_ - [logger-page](./src/client/pages/logger/logger-page.md) — _(no submodules)_- [network-broadcast](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_ - [network-broadcast-page](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_- [network-webrtc](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_ - [network-webrtc-page](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_- [network-websocket](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_ - [network-websocket-page](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_- [storage-s3](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_ - [storage-s3-page](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_- [surrealdb](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_ - [surrealdb-page](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_- [crypto-page](./src/client/pages/crypto/crypto-page.md) — _(no submodules)_- [datetime](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_ - [datetime-page](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_- [extension-test](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_ - [extension-test-page](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_- [feature-flags](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_ - [feature-flags-page](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_- [home](./src/client/pages/home/home-page.md) — _(no submodules)_ - [home-page](./src/client/pages/home/home-page.md) — _(no submodules)_- [i18n](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_ - [i18n-page](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_- [logger](./src/client/pages/logger/logger-page.md) — _(no submodules)_ - [logger-page](./src/client/pages/logger/logger-page.md) — _(no submodules)_- [network-broadcast](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_ - [network-broadcast-page](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_- [network-webrtc](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_ - [network-webrtc-page](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_- [network-websocket](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_ - [network-websocket-page](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_- [storage-s3](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_ - [storage-s3-page](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_- [surrealdb](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_ - [surrealdb-page](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_- [crypto-page](./src/client/pages/crypto/crypto-page.md) — _(no submodules)_- [datetime](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_ - [datetime-page](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_- [extension-test](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_ - [extension-test-page](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_- [feature-flags](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_ - [feature-flags-page](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_- [home](./src/client/pages/home/home-page.md) — _(no submodules)_ - [home-page](./src/client/pages/home/home-page.md) — _(no submodules)_- [i18n](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_ - [i18n-page](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_- [logger](./src/client/pages/logger/logger-page.md) — _(no submodules)_ - [logger-page](./src/client/pages/logger/logger-page.md) — _(no submodules)_- [network-broadcast](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_ - [network-broadcast-page](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_- [network-webrtc](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_ - [network-webrtc-page](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_- [network-websocket](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_ - [network-websocket-page](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_- [storage-s3](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_ - [storage-s3-page](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_- [surrealdb](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_ - [surrealdb-page](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_- [crypto-page](./src/client/pages/crypto/crypto-page.md) — _(no submodules)_- [datetime](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_ - [datetime-page](./src/client/pages/datetime/datetime-page.md) — _(no submodules)_- [extension-test](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_ - [extension-test-page](./src/client/pages/extension-test/extension-test-page.md) — _(no submodules)_- [feature-flags](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_ - [feature-flags-page](./src/client/pages/feature-flags/feature-flags-page.md) — _(no submodules)_- [home](./src/client/pages/home/home-page.md) — _(no submodules)_ - [home-page](./src/client/pages/home/home-page.md) — _(no submodules)_- [i18n](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_ - [i18n-page](./src/client/pages/i18n/i18n-page.md) — _(no submodules)_- [logger](./src/client/pages/logger/logger-page.md) — _(no submodules)_ - [logger-page](./src/client/pages/logger/logger-page.md) — _(no submodules)_- [network-broadcast](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_ - [network-broadcast-page](./src/client/pages/network-broadcast/network-broadcast-page.md) — _(no submodules)_- [network-webrtc](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_ - [network-webrtc-page](./src/client/pages/network-webrtc/network-webrtc-page.md) — _(no submodules)_- [network-websocket](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_ - [network-websocket-page](./src/client/pages/network-websocket/network-websocket-page.md) — _(no submodules)_- [storage-s3](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_ - [storage-s3-page](./src/client/pages/storage-s3/storage-s3-page.md) — _(no submodules)_- [surrealdb](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_ - [surrealdb-page](./src/client/pages/surrealdb/surrealdb-page.md) — _(no submodules)_

Overview

The testbed is a React 19 SPA served by a Hono/Bun server. The client runs library demos entirely in the browser (crypto, logger, datetime, i18n, feature-flags) or proxies requests to the server for service-backed operations (S3, SurrealDB, WebRTC signalling, WebSocket relay).

Quick Start

# From monorepo root
bun install
bun run dev --filter=@suaveplan/testbed

# Or directly in this directory
bun run dev

The app starts at http://localhost:3200.

Services

Optional services are detected at page load via a /api/*/status health check. Pages gracefully degrade when services are unavailable.

Service Env Vars Default
Garage S3 GARAGE_S3_ENDPOINT, GARAGE_S3_ACCESS_KEY_ID, GARAGE_S3_SECRET_ACCESS_KEY, GARAGE_S3_BUCKET testbed
SurrealDB SURREALDB_URL, SURREALDB_USERNAME, SURREALDB_PASSWORD root/root

Running E2E Tests

# All browsers
bun run test:e2e

# Chromium only
bunx playwright test --project=chromium

# Specific spec file
bunx playwright test e2e/libraries/crypto.spec.ts

# Extension tests (requires chromium-extensions project)
bunx playwright test --project=chromium-extensions e2e/extensions/

The Playwright web server auto-starts bun run dev on port 3200 if not already running.

Multi-Session Tests

Multi-session tests in e2e/multi-session/ open multiple browser contexts to simulate real-time scenarios:

  • broadcast-channel.spec.ts — sender broadcasts via BroadcastChannel; receiver tab receives messages. Runs entirely in-browser (no server needed).
  • websocket-gateway.spec.ts — sender and receiver connect to the WebSocket relay server at /api/ws.
  • webrtc-mesh.spec.ts — host and peer pages complete a WebRTC handshake via /api/signalling.
  • webrtc-server-failover.spec.ts — verifies graceful handling when the signalling endpoint returns 503.

All multi-session tests use createMultiSession from @suaveplan/testing-e2e/multi-session.

Extension Tests

Extension tests in e2e/extensions/ use launchWithExtension from @suaveplan/testing-e2e/extension-fixture to launch Chromium with a sideloaded test extension.

Place your test extension at e2e/fixtures/test-extension/. The extension should inject <div id="extension-injected">...</div> into the page for detection.

bunx playwright test --project=chromium-extensions

data-testid Contract

Every page exposes these stable selectors for E2E tests:

Selector Element
[data-testid="page-title"] <h1> with page/library name
[data-testid="demo-card"] Wraps interactive demo area
[data-testid="output"] Live result display
[data-testid="console-panel"] Console capture panel
[data-testid="header"] Top navigation bar
[data-testid="sidebar"] Left navigation sidebar
[data-testid="library-card"] Library cards on home page
[data-testid="locale-select"] Locale dropdown on i18n page
[data-testid="message-input"] WS/WebRTC/Broadcast input
[data-testid="send-button"] Send button on network pages
[data-testid="extension-mount"] Extension injection target
[data-status="<status>"] Connection status indicator

Console Panel

Each page has a [data-testid="console-panel"] section that documents the library operations in use. It is informational only — it does not capture actual browser console output. Console baseline tests use attachConsoleMonitor from @suaveplan/testing-e2e/console-monitor to assert no unexpected errors.

Test Control API

In test mode (NODE_ENV=test), the server mounts testControlRoutes at /api/test-control:

Method Path Description
POST /api/test-control/disable-route Temporarily returns 503 for a given API route

Used by simulateServerFailure from @suaveplan/testing-e2e/multi-session.

Env-Bridge Variables

Variable Used By Description
GARAGE_S3_ENDPOINT Storage S3 page Garage S3-compatible endpoint URL
GARAGE_S3_ACCESS_KEY_ID Storage S3 page S3 access key ID
GARAGE_S3_SECRET_ACCESS_KEY Storage S3 page S3 secret access key
GARAGE_S3_BUCKET Storage S3 page S3 bucket name (default: testbed)
SURREALDB_URL SurrealDB page SurrealDB connection URL
SURREALDB_USERNAME SurrealDB page SurrealDB username (default: root)
SURREALDB_PASSWORD SurrealDB page SurrealDB password (default: root)
PORT Server HTTP listen port (default: 3200)

Routes Manifest

Path Library/Feature Category Services
/ Testbed home home
/crypto @suaveplan/crypto crypto
/logger @suaveplan/logger observability
/datetime @suaveplan/datetime primitives
/i18n @suaveplan/i18n primitives
/feature-flags @suaveplan/feature-flags primitives
/storage-s3 Storage — S3 storage Garage
/surrealdb SurrealDB database SurrealDB
/network-webrtc Network — WebRTC network Signalling
/network-websocket Network — WebSocket network WS relay
/network-broadcast Network — BroadcastChannel network
/extension-test Extension Test extensions

Port Allocation

Port Service
3200 Testbed dev/prod server
6042 Storybook