Drive a local web app
Use the bundled Playwright driver instead of writing a one-off browser script.
It keeps one page alive while reading commands from standard input and reports
each command as OK or FAIL.
One-time setup
Resolve this installed skill's directory, then run:
cd <drive-local-webapp-skill-directory>
npm ci
npx playwright install chromium
Node.js 20 or newer is recommended. Browser binaries are cached by Playwright. Verify the installation reproducibly:
npm run self-check
Workflow
Start the target application's normal development or demo server. Use throwaway fixtures, never production or personal data. Check that the chosen port is free before binding it.
Run
node scripts/driver.mjsfrom this skill directory and pipe one command per line:DRIVER_SCREENSHOT_DIR=/tmp/webapp-shots node scripts/driver.mjs <<'EOF' nav http://127.0.0.1:8766/ wait-for text=Dashboard click button:text-is("Settings") fill input[placeholder="API token"] :: demo-token click button:text-is("Save") screenshot console --errors EOFTreat any
FAILline or non-zero exit as a failed check. Treat a non-emptyCONSOLE_ERRORSarray as a failed check unless the error is explicitly expected.Inspect the screenshot itself. File existence does not prove the intended UI rendered.
If the host sandbox blocks Chromium process creation, rerun the same driver command with the client's narrowly scoped approval mechanism. Do not reinstall Chromium or alter the application to work around a host permission error.
Only navigate to a local page you trust or a URL the user explicitly placed in scope. Headless Chromium is not a security boundary: page JavaScript can make network requests and exercise the permissions available to the browser process. Do not use this driver as a general-purpose browser for untrusted sites.
Commands
nav <url>
wait-for <selector>
click <selector>
mouse-click <x>,<y>
fill <selector> :: <value>
set <selector> :: <value>
press <key>
wait <milliseconds>
eval <javascript-expression>
screenshot [absolute-path]
console --errors
Use :text-is("Label") for exact text. Playwright's :has-text() is
case-insensitive substring matching and can silently click the wrong control.
Use mouse-click for a target with no addressable element, such as a canvas, an
embedded map, or a region inside a chart. Prefer a selector wherever one exists;
coordinates go stale as soon as the layout moves.
Keep screenshots outside the target repository. The driver defaults to the
system temporary directory, or use DRIVER_SCREENSHOT_DIR. It refuses to
overwrite an existing screenshot path.
Common failures
- Selectors containing spaces require the literal
::separator forfillandset. - Hidden tab content may exist in the DOM; click the actual tab before waiting on its contents.
- Charts initialized inside
display:nonecontainers may have zero width. Activate the tab, wait, and verify the app resizes or lazily initializes the chart. - A client-side token gate may leave the screen loading without a console error. Inspect the application's setup UI and use a demo token.
screenshotcaptures the full page, butmouse-clickaddresses the visible viewport. On a page taller than the viewport, a point measured off a screenshot is not the point that gets clicked, and a point past the viewport still reportsOKwhile hitting nothing. Scroll the target into view first, and assert on the effect rather than on theOKline.