Playwright cli setup
Skill AgentiaPT/vela-slides/.claude/skills/playwright-cli-setup
Vela Slides - AI Powered presentation App and Agent Skill
npx -y skills add AgentiaPT/vela-slides --skill playwright-cli-setupAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
THE DEFAULT tool for ad-hoc / interactive Vela browser work — explore or test the app, screenshot a state, reproduce a bug, poke a selector, drive presenter/gallery flows, verify a UX change, run bench/vela-interaction-bench.sh. Drives a live offline Vela render via Playwright CLI (@playwright/cli), a persistent warm browser you steer one command at a time (open, snapshot, click, press, eval), inspecting state + output between steps. Prefer this over the committed `vela-drive.js` scripts (skill: vela-live-render) — those are for repeatable, committed automation only (CI, benchmark, recorded demo), NOT one-off exploration. Also prefer over writing throwaway Playwright .js files. Covers container setup (blocked CDNs, file:// block, pinned Chromium) and per-agent session isolation for parallel browser tests.
SKILL.md
9.4 KB, as published. Nobody here has run it
Playwright CLI ↔ Vela setup (this container)
@playwright/cli keeps a persistent browser as a background session (-s=<name>)
and you drive it one command at a time — open, goto, snapshot, click e15,
press ArrowRight, eval "…" — reading state between steps instead of running a
blind script. window globals survive across invocations, so a helper installed
once (e.g. a timing harness) stays live. Output (snapshots, console logs) is written
to .playwright-cli/ (gitignored); read only what you need.
It is a different package from playwright and from Playwright MCP. Prefer it
here for interactive/exploratory driving; it's cheaper on context than MCP (nothing
streams into the conversation unless you read a file).
Setup — do this once per fresh container
1. Restore the locked deps, then install the CLI isolated (see Supply-chain safety below for why it's not committed):
npm ci --ignore-scripts # locked tree first
npm install --no-save --no-audit --no-fund --ignore-scripts @playwright/[email protected]
2. Config is already committed at .playwright/cli.config.json (the default
config path, read from the repo root). It solves the two things that otherwise fail:
{
"allowUnrestrictedFileAccess": true,
"browser": {
"browserName": "chromium",
"launchOptions": {
"executablePath": "/opt/pw-browsers/chromium-1194/chrome-linux/chrome",
"headless": true,
"args": ["--no-sandbox"]
}
}
}
Recreate it if missing. Update executablePath if the image's Chromium moves
(ls /opt/pw-browsers/).
Supply-chain safety (why @playwright/cli is NOT committed)
This repo pins its supply chain via a committed package-lock.json (exact
versions + sha512), npm ci --ignore-scripts in CI, npm audit gating, and a
7-day release-cooldown (.npmrc: ignore-scripts=true, see docs/SECURITY.md).
@playwright/[email protected] transitively depends on a days-old alpha of
playwright (nested in its own node_modules, so it does not override the
repo's locked top-level playwright). Committing it would pull that fresh alpha
into the audited, shipped dependency tree — against the cooldown policy. So it is
deliberately kept out of package.json / package-lock.json and installed as
an isolated, dev/test-only tool. The install is hardened accordingly:
@playwright/[email protected]— exact version pin, no range (no surprise upgrades).--ignore-scripts— blocks pre/post-install hooks (belt-and-suspenders with.npmrc: ignore-scripts=true); the #1 npm supply-chain vector.--no-save— never writes topackage.json/package-lock.json; the audited, shipped tree stays exactly as locked.npmstill verifies the registry sha512 on download.- Ephemeral & unshipped —
node_modules/is gitignored; the CLI never enters the Vela artifact, a release, or CI's locked install. - Always run
npm ci --ignore-scriptsfirst so the top-levelplaywright/etc. match the lockfile (an ad-hocnpm installearlier can drift them).
Before bumping the pin, check the new version's transitive playwright dep
(npm view @playwright/cli@<v> dependencies) and prefer a version that resolves a
stable playwright once one ships.
Drive a Vela deck
# 1. Build an OFFLINE render (transpiled, deck injected via STARTUP_PATCH).
# Do NOT serve.py / esm.sh here — the React/lucide CDNs are blocked.
node tools/vela-dev/scripts/render-offline.js examples/vela-demo.vela /tmp/vout
# 2. Open a persistent session on the render (file:// works via the config).
npx playwright-cli -s=vela open "file:///tmp/vout/render.html"
# 3. Wait for hydration (~2–4s; offline app.js transpiles then mounts).
npx playwright-cli -s=vela eval "({b:!!window.__velaBooted, blocks:document.querySelectorAll('[data-block-type]').length})" --raw
# 4. Drive it. Examples:
npx playwright-cli -s=vela press f # enter presentation (auto-selects 1st module)
npx playwright-cli -s=vela press ArrowRight # next slide
npx playwright-cli -s=vela screenshot --filename=/tmp/slide.png
npx playwright-cli -s=vela eval "(document.querySelector('[data-block-type=heading]')||{}).textContent" --raw
# 5. Housekeeping.
npx playwright-cli list
npx playwright-cli -s=vela close
npx playwright-cli kill-all # nuke stale/zombie browsers
Tip: node_modules/.bin/playwright-cli skips npx resolution overhead in loops.
Parallel agents — isolating concurrent browser tests
Multiple agents can drive Vela at once, but they share one machine-wide session
registry (~/.cache/...) and one per-cwd output dir. Verified isolation ladder,
weakest → strongest:
| axis | how | what it isolates | gotcha |
|---|---|---|---|
| session name | -s=agentA vs -s=agentB | the browser + page state (independent — one advancing slides doesn't move the other) | same registry & same .playwright-cli/ output dir |
| registry | PWTEST_DAEMON_SESSION_DIR=/tmp/agentA-daemon (per agent) | the whole session daemon — an agent's sessions are invisible to another's list/close-all/kill-all | must set it on every command for that agent |
| workspace/cwd | run each agent from its own dir / git worktree | session namespace and the .playwright-cli/ output dir (both keyed off findWorkspaceDir(cwd)) | needs a separate checkout |
Recommended patterns
- Best: give each parallel agent its own git worktree (the Agent tool's
isolation: "worktree"). Different cwd ⇒ different workspace ⇒ separate session namespace and separate.playwright-cli/output, for free. Add a unique-s=<name>per agent too. - Same cwd: give each agent a unique
-s=<name>and a uniquePWTEST_DAEMON_SESSION_DIR— otherwise one agent'sclose-all/kill-allkills every agent's browsers (the single biggest footgun). Output files still land in the shared.playwright-cli/(timestamped names rarely collide, but they intermix).
Two things that make this easy here: the offline file:// render needs no
port, so parallel sessions never collide on a port (unlike serving over HTTP); and
each deck can render to its own /tmp/<agent>-render dir. Cost: each session is a
full Chromium (~200-300 MB RAM) — cap concurrency to the box, and never use the global
close-all/kill-all from a parallel agent unless its registry is isolated.
Vela-specific hooks & selectors (for eval / interaction)
| what | how |
|---|---|
| boot done | window.__velaBooted === true and #root innerText non-empty |
| headless UI battery | await window.__velaRunUITests() (see part-uitest.jsx) |
| current slide blocks | [data-block-type] elements (text signature changes on nav) |
| current heading | [data-block-type=heading] |
| in presentation? | <header> is removed while presenting |
| gallery open? | [data-testid=gallery-close] present |
| gallery scroll rail | [data-scroll-container] |
| enter/exit present | key f (or F5); Escape exits |
| next / prev slide | ArrowRight / ArrowLeft |
| open gallery | key g |
Note: window.__velaGetCurrentSlide is gated behind local mode (off in offline
renders) — use the DOM signals above instead.
Gotchas already solved (don't rediscover)
file://is blocked by default → "Access to file: protocol is blocked". Fixed byallowUnrestrictedFileAccess: truein the config (or envPLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS=1). Config is read atopentime — if you change it,closeand re-openthe session.- Bundled playwright-core wants a newer Chromium than the image ships
(expects ~1229, image has 1194) → the CDN download is blocked, so the config's
executablePathpoints at the pinned build. Don't runplaywright install. - CDNs (esm.sh / React / lucide / Babel) are blocked → never use
serve.py's default importmap HTML; alwaysrender-offline.js(vendored UMD + Node transpile). - One harmless console error on every load (
ERR_INVALID_URL/ font fetch) — ignore it; the app renders fully. - Don't
pkillbackground jobs from a Bash tool here — the sandbox may SIGKILL the shell (exit 144). Useplaywright-cli close/kill-allfor browsers.
Ready-made benchmark
bench/vela-interaction-bench.sh uses exactly this setup to measure slide-nav /
interaction latency and thumbnail-scroll FPS. See bench/README.md.