Files
clide/docs/testing/claude-ui-workflow.md
T
jpmschweitzerandClaude 77e395b3f2 document test layers and Claude's UI workflow
Three docs under docs/testing/:

  * README.md — layer-by-layer reference (what each covers, the
    runner, how to invoke, local vs CI flow). Points at the load-
    bearing startup gate and the client-side-only constraint so a
    macOS clone runs the same suite as Linux.
  * a11y-manual.md — 15-minute manual checklist run at every tier
    cut. Orca on Linux, VoiceOver on macOS. Catches prose drift
    automation can't judge.
  * claude-ui-workflow.md — how Claude Code drives the WASM build
    through Playwright, including the `flt-semantics-placeholder`
    quirk (semantics are opt-in in Flutter web; the driver
    auto-clicks the placeholder to enable them).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-21 15:50:05 +02:00

2.8 KiB

How Claude Code uses clide

Claude Code needs to actually use the app while building features. The pipeline:

  1. Flutter builds the app to WASM. flutter build web --wasm ships a CanvasKit/Skwasm bundle under app/build/web/.
  2. A local server serves it. tools/ui/serve.sh starts http://localhost:4280 in the background with a pidfile.
  3. Playwright drives a headless Chromium. Instead of click-by-pixel — which is brittle on a CanvasKit <canvas> — the driver queries Flutter's semantic tree (flt-semantics[aria-label=…]), the same tree screen readers use. This is only reliable because every interactive widget in clide already ships Semantics(label:, hint:) wrappers for a11y — the automation surface is a free side-benefit.

Claude's one-liners

# Bring the app up (builds wasm, starts server)
make ui-dev

# Run an ad-hoc probe
cd tools/ui && npx playwright test ...

# Everything in one shot (build + serve + smoke + stop)
make ui-smoke

# Bring it down
make ui-stop

Writing a driver script

import { test, expect } from '@playwright/test';
import { ClideDriver } from '../driver';

test('opens the theme picker and selects summer-night', async ({ page }) => {
  const clide = new ClideDriver(page);
  await clide.goto('/');

  // Keyboard shortcut that invokes `theme.pick`:
  await page.keyboard.press('Control+K');

  // Pick via semantic label.
  await clide.click('summer-night');

  // Dump the whole tree to inspect state after an interaction.
  const tree = await clide.dumpSemanticsTree();
  console.log(JSON.stringify(tree, null, 2));

  // Screenshot into out/ (Claude reads the PNG via the Read tool).
  await clide.screenshot('out/theme-picker.png');
});

Prerequisites (one-time per machine)

cd tools/ui
npm install
npx playwright install chromium

Known quirks

  • Labels are merged. Flutter web concatenates sibling Semantics labels into a single aria-label separated by newlines. byLabel uses substring match for that reason. When two Semantics nodes share a substring, narrow with .filter() on the returned locator.
  • The placeholder button. Flutter web ships semantics disabled by default, behind an invisible <flt-semantics-placeholder> button. ClideDriver.waitUntilReady() clicks it automatically.
  • No daemon on web. The WASM build has no unix socket; the status indicator always says disconnected. That's honest — a web-hosted clide has no local daemon to talk to. The Playwright flow is for UI-only verification.

When Playwright is overkill

If all I need is "does the extension register its contributions," a widget test (test/builtin/*/widget_test.dart) is faster and cheaper. Reach for Playwright when the question is about the real rendering pipeline, keyboard behavior, or multi-widget interactions that mirror a user workflow.