Files
clide/tools/ui
jpmschweitzerandClaude Fable 5 41af83f024 repoint the UI harness at the repo root; CI via make targets (T-384)
tools/ui/build.sh and serve.sh still cd'd into the app/ directory the
flattening removed, so make test-e2e / ui-dev / ui-smoke died at the
first line. The staged Gitea workflow had the same stale cd in every
job, plus a coverage gate with no coverage run before it — it now
goes through the make targets (tooling discipline: the make layer owns
env setup) with make test-coverage feeding make coverage-gate.

Fixing the paths exposed the real break: flutter build web --wasm
cannot compile the tree since the dart:ffi pivot (tree-sitter, native
PTY) — dart:ffi does not exist on the wasm target. Fence vs park vs
drop is filed as Q-50; the workflow's e2e job is withheld with a
pointer there, and T-384 sits in review until Q-50 resolves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:39:24 +02:00
..

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

cd tools/ui
npm install
npx playwright install chromium

Dev loop

# 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:

make ui-smoke                      # build + serve + run smoke + stop

Driver surface

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.