uiscout
Graph-driven frontend testing

Point it at your app. It walks every screen.

uiscout starts your app, signs in once, clicks every safe control, types into every field and follows every screen it can reach, then judges each step with deterministic checks. You get a map of the app and a list of real defects, each with the path that shows it. No spec to write, no model, the same result on every run.

pnpm exec uiscout init && pnpm run scout:quick
.uiscout/graph.html · example shop8 screens · 1 error
Screens reached from the entry, in columns by steps from the entry ENTRY 1 STEP 2 STEPS
node / (depth 0) click Products → /products ok click Pricing → /pricing ok click Place order → /checkout [Confirm] network POST /api/orders returned 500
1command to set up: uiscout init reads the framework, port, routes and paid APIs to block
34 / 35navigation patterns observed on uigraph's routing gauntlet, with a five-line config
~45 sfor --quick on a signed-in app: 26 screens from 12 routes, dev server start included
25 sfor an affected-only pull request run, against 42 s for the whole app

What it catches

The defects that slip past unit tests and nobody clicks through before a release: found by walking the app the way a curious user would, after every single step.

Crashes and console errors

An exception on a menu nobody opens in tests, a library warning that means a broken component, an unhandled promise.

Broken requests

A 500 behind a button, a 404 from a stale link, a call that fails only from one screen.

Dead controls

A button under an invisible overlay, a link that goes nowhere, a click that can't land.

Layout and accessibility

Overlapping controls, clipped labels, buttons with no accessible name.

Regressions

A control that disappeared, a renamed action, a link that now leads somewhere else, all as a reviewable git diff.

Broken business rules

"An empty cart can't be ordered", "a guest never reaches checkout": your rules, checked on every path walked.

From setup to a fix

Built to fit the loop you already have: one command to set up, a sign-in done once, and every finding turned into something you can act on.

Set up in one command

uiscout init writes the config: how to start the app, its routes from TanStack Router or Next.js, and the AI, payment and analytics hosts the walk must never call. Works from any folder of a monorepo.

Signed in, once

auth steps run in a fresh browser before the walk; every screen starts from that session. Passwords come from ${ENV} variables and never reach a report.

Every error, reproducible

A one-line repro (open / → click "Cart" → click "Pay") and the control's source file and line. uiscout export 1 writes it as a Playwright test.

Forms that let it through

Fields get values they accept: emails, numbers within min and max, dates, values matching pattern. The same text every run.

Feedback while you code

check --watch reruns on save and, with a baseline, walks only the screens the changed files build.

A page per pull request

A comment with the verdict and the graph diff, and uiscout site to host the graph for each pull request.

Three kinds of check, one walk

Every step the walker takes is judged three ways. The first needs nothing from you. The other two grow with the project.

ORACLE A · ZERO SETUP

Is the app broken?

Generic checks that hold for every web app.

  • scriptuncaught exceptions, console.error
  • network5xx, unexpected 4xx, failed calls
  • dead-controla click that can't land
  • layoutoverlaps, clipped text
  • a11yserious axe rules
ORACLE B · YOUR RULES

Does it do the right thing?

Business rules in TypeScript, checked on every path. Product people write the sentences in intent files; the report shows which are covered.

  • alwaysan invariant in every state
  • when().then()an implication
  • eventually().within()a deadline, on the app's clock
ORACLE C · BASELINE

Is it different from yesterday?

An accepted run is committed as a lockfile. Every later run is compared to it, and git diff uiscout/ is the review.

  • errora control gone, renamed, or leading elsewhere
  • warninga control moved more than 16 px
  • infoa new control

How fast it is

About 0.4–2 s per step: each step waits until the network is idle and the page has been still for 250 ms, never for a fixed timeout. Screens run four at a time, so a run takes about as long as its busiest screen.

RunWhat it walksTime
check --quick, signed in, seeds: ["auto"]26 screens, 82 steps, dev server start included~45 s
check --depth 1 --no-a11y --fast-forward 011 screens, 186 steps~1 min
check (depth 1, all checks)11 screens~2 min
check --depth 231 screens, 431 steps~4 min
check --affected origin/main5 of 10 screens, from a one-file change25 s (vs 42 s)
check --depth 3, no configa 10-route sample app, 19 clicks7 s
  • Deterministic. A fixed clock and recorded API responses give the same graph on every run of the same commit: a baseline reran with no diff at 31 screens and 352 edges.
  • No backend in CI. --mode replay answers every API call from recordings, and fails closed: nothing reaches a real server.
  • No model, no API key. Every judgement is a deterministic check; AI agents can read the graph over MCP, but never decide a result.

Start in a minute

Any web app: uiscout starts its dev server, or walks one already running (a preview build, staging).

pnpm add -D github:nguyentrinhquy1411/uiscout
pnpm exec playwright install chromium
pnpm exec uiscout init        # config, scripts, .gitignore
pnpm run scout:quick          # the first report

Using Claude Code? Install the skill and ask it to find and fix the bugs: /plugin marketplace add nguyentrinhquy1411/uiscout, then /plugin install uiscout@uiscout. More.

Then commit a baseline with uiscout check --update, and add the GitHub Actions job so every pull request gets a report comment.

Where it fits

Hand-written end-to-end tests check the paths someone thought of. Static graph tools read routes from code and stop at routers they don't support. uiscout watches what the running app actually does.

uiscoutPlaywright specsuigraph
Builds the graph byWalking the running app—Static analysis, then verify
Works without a router adapterYesYesNo (0 screens on TanStack Router)
Finds defects with no test writtenYesNoNo
Catches regressionsBaseline lockfilePer assertionGraph diff
Drag, resize, canvas widgetsAdapters with invariantsHand-written—
Coding agentsMCP, quarantined proposals—MCP, quarantined proposals
Ranks gaps by real trafficYesNoNo

Also compared with session replay (Meticulous), AI test writers, browser agents and Crawljax: How it compares.