Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Playwright Development Environment

Code name: playwright-dev

OS-level dependencies for headless Chromium, Firefox, and WebKit, a browser-binary cache shared across rebuilds via a Docker named volume, and the official Playwright Test VS Code extension — pre-configured so npx playwright test and npx playwright install just work, without re-downloading browsers on every rebuild.

Also included automatically: repairs broken host paths in your git config and restores your SSH commit-signing key on every attach, on both local and cloud containers, with nothing to set up on your end — see helpers4-common for how it works.

Why this feature exists

Headless browser automation (Playwright, and anything built on Chromium's DevTools Protocol — including WebAuthn.addVirtualAuthenticator, useful for testing passkey/WebAuthn flows without physical hardware) needs a real browser binary plus a long list of OS shared libraries. npx playwright install --with-deps can fetch both, but:

  • it needs sudo/root at test-run time to apt-install system packages,
  • it re-downloads the browser binaries into ~/.cache/ms-playwright on every fresh container, since that path isn't normally persisted.

This feature moves both steps into the devcontainer lifecycle: OS packages are installed once at image build time (via the official playwright install-deps, not a hand-maintained apt list that would drift across base-image OS versions), and the browser binaries are downloaded once into a named volume that survives rebuilds — the same shape as the pnpm-store feature.

It deliberately does not install the playwright npm package itself — that stays a devDependency of the consuming project, so the CLI version always matches the project's own Playwright version instead of drifting from a separately-installed global one.

Alternative: Microsoft's prebuilt Playwright image

Microsoft publishes mcr.microsoft.com/playwright:v<version>-<os>, a Docker image with browsers and OS deps already baked in — zero install time, nothing to cache. If you don't need to compose with other helpers4 features on top of your own base image, using that image directly as "image" is simpler than this feature. The tradeoff is that it pins your whole devcontainer to Microsoft's base image and Playwright version, instead of adding browser support to whatever base image and feature set (typescript-dev, vite-plus, pnpm-store, …) you're already using.

Usage

Add this feature to your devcontainer.json:

{
    "features": {
        "ghcr.io/helpers4/devcontainer/playwright-dev:1": {}
    }
}

This will:

  1. Install the OS packages required to run Chromium, Firefox, and WebKit headless
  2. Set PLAYWRIGHT_BROWSERS_PATH to a Docker-volume-backed path shared across rebuilds
  3. Download the browser binaries into that volume on first container start
  4. Install the official Playwright Test VS Code extension, pre-configured

Chromium only

If your project only needs Chromium (e.g. CDP-based WebAuthn testing), skip the Firefox/WebKit dependencies to keep the image smaller:

{
    "features": {
        "ghcr.io/helpers4/devcontainer/playwright-dev:1": {
            "browsers": "chromium"
        }
    }
}

Options

Option Type Default Description
browsers string (chromium | firefox | webkit | all) all Which browser engine(s) to install OS-level dependencies for, and to pre-download into the shared cache.
installDeps boolean true Install the OS packages required to run the selected browser(s) headless, via playwright install-deps. Disable if the base image already provides them.

IDE support

Editor Status ID
VS Code ms-playwright.playwright
Cursor ms-playwright.playwright (same registry as VS Code)
WebStorm / IntelliJ IDEA (2023.3+) ℹ️ Playwright tests are recognized natively via the built-in Test Automation plugin — not something this feature installs, nothing to configure here.
Zed 🔜 no standard devcontainer customization format yet

Browser cache volume

Binaries live in a Docker named volume (helpers4-playwright-browsers-${localEnv:USER}) mounted at /usr/local/share/playwright-browsers, exposed to every shell via PLAYWRIGHT_BROWSERS_PATH. The volume name includes ${localEnv:USER}, so it's shared across every local devcontainer for the same host OS user — not just rebuilds of one project — the same reasoning as pnpm-store: browser binaries are downloaded, versioned artifacts with no credentials attached, so sharing them across your own projects has none of the cross-project bleed risk that keeps an AI tool's credentials volume (claude-dev, mistral-dev) scoped per devcontainer instead.

A postCreateCommand guard script takes ownership of the volume at container creation (with --shared, since a second, concurrently-running project can resolve a different container UID) and downloads the browsers only if they haven't been fetched yet for the current Playwright version + browsers selection — the version is part of the completion marker specifically because the cache is now shared: two projects pinning different Playwright versions can need different browser revisions, and only the marker (not Playwright's own revision-namespaced cache layout) would otherwise get that wrong.

# Confirm what's cached
ls "$PLAYWRIGHT_BROWSERS_PATH"

VS Code Extension Included

Playwright Test for VS Code (ms-playwright.playwright)

  • Test explorer: run/debug Playwright tests from the sidebar
  • Pick locators, record new tests (codegen), and view traces inline

Settings applied:

{
  "playwright.reuseBrowser": true,
  "playwright.showTrace": true
}

Testing a WebAuthn/passkey flow without hardware

Chromium's DevTools Protocol exposes WebAuthn.addVirtualAuthenticator — no extra software authenticator needed. With Playwright:

const client = await context.newCDPSession(page);
await client.send("WebAuthn.enable");
await client.send("WebAuthn.addVirtualAuthenticator", {
  options: {
    protocol: "ctap2",
    transport: "internal",
    hasResidentKey: true,
    hasUserVerification: true,
    isUserVerified: true,
  },
});

Works Great With

{
  "features": {
    "ghcr.io/helpers4/devcontainer/playwright-dev:1": {},
    "ghcr.io/helpers4/devcontainer/typescript-dev:1": {},
    "ghcr.io/helpers4/devcontainer/vite-plus:1": {},
    "ghcr.io/helpers4/devcontainer/pnpm-store:1": {}
  }
}

Troubleshooting

Browsers didn't download on first start

The guard script needs network access at postCreateCommand time. Re-run it manually, or install directly:

npx playwright install

install-deps fails on an unsupported base image

playwright install-deps only knows the apt package names for the Debian/Ubuntu versions Playwright officially supports. Pin your base image to one of those, or set installDeps: false and install the equivalent packages yourself.

Links

Version History

  • v1.3.1: Code-review follow-up on v1.3.0's cache-sharing change — a failed Playwright version lookup (offline, transient npx/registry hiccup) fell back to a generic .h4-installed-unknown-* marker; on the now-shared volume, a different project hitting the same failure could match that marker and wrongly skip installing a browser revision it doesn't actually have. A failed lookup no longer participates in the marker shortcut at all — it just re-runs the (idempotent, revision-aware) install for that one start instead.
  • v1.3.0: The browser-cache volume is now keyed by ${localEnv:USER} instead of ${devcontainerId} — shared across every local devcontainer for this host OS user instead of exclusive to one project, avoiding redundant downloads across your own projects. Safe unlike an AI tool's credentials volume: browser binaries carry no identity/permissions surface to leak between projects. h4_ensure_volume_writable is now called with --shared. The download completion marker is now also scoped by the resolved Playwright version (previously just the browsers selection) — necessary now that the cache is shared, since two projects can pin different Playwright versions needing different browser revisions; without this, whichever project populated the shared cache first would cause a different project's version to wrongly skip its own download.
  • v1.2.3: Internal refactor, no behavior change — the browser-cache ownership logic (chown to the current user when needed) now calls helpers4-common's h4_ensure_volume_writable instead of carrying its own inline copy.
  • v1.2.2: Documentation only, no functional change — the previous wording sweep made the JSON description field far too long, shifting focus away from the feature itself onto the self-heal side benefit. Shortened to 5 words and kept generic (no implementation detail like "git config"), matching the original's brevity and level of detail.
  • v1.2.1: Documentation only, no functional change — the self-heal callout above (and the JSON description field) led with internal jargon ("helpers4's self-heal") instead of the actual benefit; reworded to lead with what it does, with the full mechanism staying in helpers4-common's own README.
  • v1.2.0: Documentation only, no functional change — mentions that helpers4-common's automatic git-config self-heal (see above) now comes along with this feature.
  • v1.1.0: Switched from an inline copy of helpers4-common's bootstrap (user detection, apt helpers) to a direct dependsOn on the helpers4-common feature — no behavior change, just a single source of truth for that logic instead of a copy every feature had to keep in sync.
  • v1.0.1: Dropped the @latest pin from both npx playwright calls — it was forcing the newest registry release instead of the project's own pinned version. Added ghcr.io/devcontainers/features/node:1 to dependsOn: typescript-dev alone doesn't install Node, so the npm not found guard wasn't actually covered. The browser-cache guard now writes a completion marker instead of trusting a non-empty directory, so an interrupted download doesn't get stuck as a permanently broken cache. Added an "IDE support" table and documented the mcr.microsoft.com/playwright prebuilt-image alternative.
  • v1.0.0: Initial release.

License

LGPL-3.0 - See LICENSE file for details