add web WASM Playwright harness for UI driving
tools/ui/ — scripts + Playwright config that let Claude Code (and
humans) drive the Flutter WASM build in a real browser. The point is
to avoid screenshot round-trips: every interactive widget in clide
ships a Semantics wrapper anyway (a11y requirement), so the automation
layer just queries the `flt-semantics[aria-label]` DOM.
* build.sh — `flutter build web --wasm` from app/
* serve.sh — `python3 -m http.server 4280` in the background.
Before binding, kills any stale listener on the port (orphans
from earlier failed runs no longer accumulate).
* stop.sh — port-based kill; escalates to SIGKILL after 300ms.
The pidfile is now advisory — port ownership is the source of
truth.
* driver.ts — `ClideDriver` class with `byLabel`, `click`, `type`,
`readText`, `screenshot`, `dumpSemanticsTree`, and
`waitUntilReady` that auto-clicks the `flt-semantics-placeholder`
so the semantic tree is populated before queries.
* tests/smoke.spec.ts — first driver test; asserts welcome +
disconnected labels surface in the Semantics DOM.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# clide UI harness
|
||||
|
||||
A Playwright-based driver that drives the Flutter WASM build of clide
|
||||
through its semantic DOM tree (the same tree screen readers use).
|
||||
Used by Claude Code to "actually use the app" without screenshot
|
||||
round-trips, and by CI to catch web-regression bugs.
|
||||
|
||||
## One-time setup
|
||||
|
||||
```bash
|
||||
cd tools/ui
|
||||
npm install
|
||||
npx playwright install chromium
|
||||
```
|
||||
|
||||
## Dev loop
|
||||
|
||||
```bash
|
||||
# From repo root:
|
||||
make ui-dev # build web + start local server :4280
|
||||
cd tools/ui && npx playwright test
|
||||
|
||||
# When done:
|
||||
make ui-stop # kill the local server
|
||||
```
|
||||
|
||||
Or the one-shot smoke:
|
||||
|
||||
```bash
|
||||
make ui-smoke # build + serve + run smoke + stop
|
||||
```
|
||||
|
||||
## Driver surface
|
||||
|
||||
```ts
|
||||
import { ClideDriver } from '../driver';
|
||||
|
||||
test('...', async ({ page }) => {
|
||||
const clide = new ClideDriver(page);
|
||||
await clide.goto('/');
|
||||
|
||||
await clide.click('Open project');
|
||||
await clide.type('Name', 'My project');
|
||||
const text = await clide.readText('disconnected');
|
||||
|
||||
await clide.screenshot('out/my-state.png');
|
||||
const tree = await clide.dumpSemanticsTree();
|
||||
});
|
||||
```
|
||||
|
||||
All lookups go through Flutter's semantics tree (`flt-semantics[aria-label]`).
|
||||
This only works because every interactive widget in clide emits a
|
||||
`Semantics(label:, hint:, button:)` wrapper — a requirement that's baked
|
||||
in for screen-reader support and gets enforced by
|
||||
`test/a11y/semantic_coverage_test.dart`.
|
||||
|
||||
## CI
|
||||
|
||||
`make ui-smoke` is the CI entry — builds the WASM bundle, runs the
|
||||
harness, cleans up. Enabling Gitea Actions will start running this on
|
||||
every push.
|
||||
Reference in New Issue
Block a user