CommunityCoding & Developmentgithub.com

control-ui-e2e

Design, test, fix, or extend OpenClaw Control UI GUI; UI stress-test galleries with feedback inputs, Vitest + Playwright end-to-end checks, mocked Gateway flows, screenshots/videos, agent-verifiable browser proof.

What is control-ui-e2e?

control-ui-e2e is a Claude Code agent skill that design, test, fix, or extend OpenClaw Control UI GUI; UI stress-test galleries with feedback inputs, Vitest + Playwright end-to-end checks, mocked Gateway flows, screenshots/videos, agent-verifiable browser proof.

Works with✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/clawdbot/clawdbot/tree/main/.agents/skills/control-ui-e2e

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Control UI E2E

Control UI design feedback + real browser flows with deterministic Gateway data.

UI Stress Test

  • Substantial UI changes: build local HTML stress-test gallery early; let user compare meaningful states and give feedback on concrete examples.
  • Use for layouts, interactions, components with multiple states. Small copy/icon edits may skip when gallery adds no useful comparison.
  1. Derive examples from affected components + data contracts. Cover relevant normal, loading, empty, error, unavailable, permission, selected, expanded states; long text/dense content where they stress layout. Show mutually exclusive states separately; label proposed states unsupported by current implementation.
  2. One browser-openable HTML overview: stable example IDs, short state labels, enough context to understand each example. Prefer real components + deterministic mock fixtures. Label static/approximate renderings; link running UI for interactions they cannot reproduce. Generated galleries: task-owned artifact storage; no default commit.
  3. Every example: labeled feedback input. Persist locally across refreshes with gallery-specific storage key + stable example IDs. Copy feedback action exports example IDs, state labels, comments as Markdown/plain text for user to return to conversation.
  4. Open gallery in available preview browser; share URL/file path. Preserve same gallery, example IDs, existing comments through iteration. Apply user feedback to both gallery + implementation; keep comparable.
  5. Before requesting feedback: inspect rendered examples at relevant viewport sizes; verify feedback survives refresh + exports correct example labels. Report unsupported states/preview limitations.

Gallery supports design review; never replaces focused behavior tests or inspected before/after proof from running UI. Preserve final proof in fresh capture directories below, separate from evolving gallery.

Test Shape

  • Full GUI flows: ui/src/**/*.e2e.test.ts.
  • ui/src/test-helpers/control-ui-e2e.ts: start Vite Control UI + install mocked Gateway WebSocket.
  • Deterministic scenarios. No live provider keys, real channel credentials, real Gateway unless user explicitly asks for live proof.
  • Narrow rendering logic: prefer existing .browser.test.ts/unit tests. E2E lane: routing, app boot, Gateway handshake, requests, visible UI behavior together.

Commands

  • Target one E2E test in Codex worktree:
node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/e2e/chat-flow.messaging.e2e.test.ts
  • Whole local lane in normal checkout:
pnpm test:ui:e2e
  • Use ready dependency installation or prepared normal checkout; never reconcile shared install while other jobs use it.
  • Follow $openclaw-testing: trusted development proof may run locally; remote proof needs browser/platform, clean-environment, or source-isolation reason.

Visual Proof Default

  • Appearance changes: inspected before/after visual evidence.
  • Other behavior: clearest appropriate boundary proof; video + screenshot set not mandatory when assertions already demonstrate change.
  • Deterministic Vitest E2E assertions; never commit generated screenshots/videos.
  • Shared suite disables Chromium partial rasterization + GPU rasterization to avoid observed paint-history-dependent edge pixels. Exact repeat comparisons still required before claiming reproducibility.
  • Transient states such as Saved: install page.clock before fixture; pause with pauseVirtualClock; advance only fixture work needed to enter state. setFixedTime alone does not pause timers. Capture readiness uses native layout delivery without advancing fixture clock.
  • Stills: takeControlUiScreenshotFrame from ui/src/test-helpers/control-ui-e2e-screenshot.ts, explicit semantic content + animations: "disabled". Pass viewport changes/intended scroll target through viewport/scrollTo; verifies retained centering, viewport/scroll-clip intersection, settled layout/fonts/visible images.
  • Pass all related element locators in elements; save returned page PNG + crops from that single frame. Crops enclose fractional bounds in measured PNG pixels; dimensions + unchanged bounds asserted. Never combine separate page/locator screenshots as same-frame evidence.
  • Default current-frame mode + existing viewport/element helpers preserve recording + sampled animation behavior. Static preparation stays active through bounds measurement + unclipped capture; full-page proofs must fit viewport-frame dimensions.
  • After/alongside focused E2E test: run mocked Control UI app when available, e.g. pnpm dev:ui:mock -- --port <port>.
  • Drive Chromium with Playwright against local mock URL. Capture needed states with screenshots/short video.
  • Use browser.newContext({ recordVideo: { dir, size }, viewport }), page.screenshot({ path }); close context before reporting video path.
  • Session-host command-state proof: viewport-only captures, verified with Playwright 1.62.1 + Chrome 151.0.7922.34 (Linux real Gateway; macOS arm64 synthetic reproduction). Other recording owners not migrated/certified by this fix; verify their required screenshot content + finalized video separately. See the verified capture path and upstream limitation.
  • Allocate retained proof with createControlUiE2eArtifactDir(scope, parentDir?) from ui/src/test-helpers/control-ui-e2e-artifacts.ts. Each call atomically creates fresh directory + logs actual path. Parent precedence: explicit parent → trimmed existing OPENCLAW_UI_E2E_ARTIFACT_DIR → repository's .artifacts/control-ui-e2e parent. Existing custom output controls select parents; never add/rewrite env vars to enable capture.
  • Allocate during test/scenario or beforeEach, once per attempt; standalone scripts once per invocation. Pass owner explicitly to shared capture helpers. Keep original gates, feature/stage names, viewports, waits, recording options. Distinct stages: distinct filenames. Keep screenshots/reports/video together.
  • Retain successful + failed evidence. Report actual allocated paths, including relocated filename overrides. Manually delete only exact owned directories after review; never clear shared parents before replay. Disposable build/media fixtures + owned temporary raw video may keep cleanup. New synthetic captures cannot recover overwritten evidence.
  • Timeout diagnostics: fresh children beneath existing diagnostic parent. Mantis retains every capture attempt under invocation-owned directory; refuses report overwrite. Real-Gateway suites, chat-outbox-*, chat-attachment-read-lifecycle: separate owners; coordinate before claiming replay-safe retention there.
  • Recording = validation, not only demo capture. Recorder fails/shows surprising behavior: stop, fix behavior, add/update regression test, rerecord.
  • Visual proof blocked: state exact blocker; still report textual E2E evidence.

Mock Pattern

Start app server; install mock before page.goto; assert Gateway traffic + visible UI:

const server = await startControlUiE2eServer();
const page = await context.newPage();
const gateway = await installMockGateway(page, {
  historyMessages: [{ role: "assistant", content: [{ type: "text", text: "Ready." }] }],
});

await page.goto(`${server.baseUrl}chat`);
await page.locator(".agent-chat__composer-combobox textarea").fill("hello");
await page.getByRole("button", { name: "Send message" }).click();

const request = await gateway.waitForRequest("chat.send");
await gateway.emitChatFinal({ runId: String(request.params.idempotencyKey), text: "Done." });
await page.getByText("Done.").waitFor();

New flow needs more Gateway surface: extend installMockGateway with typed scenario options/method responses.

Run Inspector evidence

  • Collect with withControlUiRunInspector from ui/src/test-helpers/control-ui-run-inspector.ts. Owns separate page; closes on success/failure; leaves caller's Chat page + unsent draft in place.
  • preparePage: mock Gateway or campaign's existing per-tab authentication setup. Shared browser context does not copy another tab's session-storage token.
  • Helper uses rendered component model's RunInspectorSelector + activityRunInspectorSelectorHref, including selected receipt's decision cursor.
  • Rendered panel's data-run-id + data-execution-id: returned present identity, not just requested URL. Selected detail's data-receipt-selector-id = DecisionReceiptDisplayV1.selectorId.
  • Missing/ambiguous identity or missing selected receipts must not count as matches.
  • Decision selectors separate from optional terminal transcript key agent.wait.terminalReceipt.assistantTranscriptIdempotencyKey. Never manufacture key from DOM selector/history row or claim Inspector evidence repairs its absence.
  • Sidebar run state's current accessible label: Active run, not Running.

Standalone Recording

  • Narrated captions, eased target zooms, fast-forwarded pauses: use proof-video dev skill. Standalone template records raw evidence + cue sidecar, renders captioned MP4 with system ffmpeg. Keep raw capture; attach inspected polished video.
  • Already-running mocked Control UI URL: temporary Playwright script or playwright test spec; keep recording flow focused:
    • Open mock URL; interact through stable data-*/user-facing role selectors; wait on asserted states, not fixed sleeps.
    • Request-driven flows: assert visible UI + mocked Gateway traffic. Example: expected count/row visible + sessions.list called with expected search, offset, limit.
    • Short sleeps only after assertions for readable captured video.
    • Generated video: invocation's fresh allocated directory. Never commit or remove older captures.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

1password

Set up and use 1Password CLI for sign-in, desktop integration, and reading or injecting secrets.

acp-router

Route plain-language requests for Claude Code, Cursor, Copilot, OpenClaw ACP, OpenCode, Gemini CLI, Qwen, Kiro, Kimi, iFlow, Factory Droid, Kilocode, or explicit ACP harness work into either OpenClaw ACP runtime sessions or direct acpx-driven sessions ("telephone game" flow). For coding-agent thread requests, read this skill first, then use only `sessions_spawn` for thread creation. Codex chat binding defaults to the native Codex app-server plugin unless ACP is explicit or background spawn needs ACP.

add-model-provider

Add and live-prove a model provider with non-interactive config one-liners, without exposing credentials.

agent-transcript

Requested GitHub PR/issue agent transcripts: redact, trim, preview, and insert safely.

apple-notes

Create, view, edit, delete, search, move, or export Apple Notes via the memo CLI on macOS.

apple-reminders

List, add, edit, complete, or delete Apple Reminders and reminder lists via remindctl.

auto-qa

Run OpenClaw-wide autonomous QA and live/stress campaigns across independent subsystem lanes, with verified fixes and a resumable evidence report.

autoreview

Structured code review through the shared OpenClaw agent-skills installation.

bear-notes

Create, search, and manage Bear notes via grizzly CLI.

blacksmith-testbox

Run Blacksmith Testbox for CI-parity checks, secrets, hosted services, migrations, or builds local cannot reproduce.

block-kit

Use proactively for structured or interactive Slack replies, and when asked to author or validate native Slack Block Kit JSON.

blogwatcher

Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.

blucli

BluOS CLI (blu) for discovery, playback, grouping, and volume.

bluebubbles

Send and manage iMessages via BlueBubbles, including attachments, tapbacks, edits, replies, and groups.

browser-automation

Use when controlling web pages with the OpenClaw browser tool, especially multi-step flows, login checks, tab management, or recovery from stale refs/timeouts.

camsnap

Capture frames or clips from RTSP/ONVIF cameras and local webcams, including USB pan/tilt/zoom control.

canvas

Present HTML on connected OpenClaw node canvases, navigate/eval/snapshot, and debug canvas host URLs.

channel-message-flows

Use when running QA Lab channel message flow evidence.

clawdtributor

Clawtributor PRs here, last week or another window: discover conversation refs, recheck GitHub, rank by impact.

clawhub

Search ClawHub for plugins by default, or skills when explicitly requested; install, verify, update, uninstall, publish, or sync skills.

Related Skills