A Dagger module — written in the .dang module language —
that runs Playwright browser tests against your
project, with first-class workspace service
wiring: point it at
any module function that returns a Service and your tests run against it.
| Function | Description |
|---|---|
test |
Run the test suite, optionally sharded across parallel containers (a @check). |
report |
Run the suite tolerating failures; returns the HTML report Directory. |
base |
The prepared test container (project mounted, deps installed, service bound). |
imageAddress |
The resolved Playwright image (useful to debug version derivation). |
Install the module in your workspace:
dagger install github.com/dagger/playwrightRun the tests:
dagger check # run every check in the workspace
dagger check playwright:test # just the Playwright suiteExport the HTML report after a failing run:
dagger api call playwright report -o ./playwright-reportIf another module in your workspace serves your app, wire it into the tests in
dagger.toml — no glue module needed:
[modules.playwright.settings]
service = "myapp:serve"The service is bound into the test container as frontend (configurable via
serviceHostname) and PLAYWRIGHT_BASE_URL is set to its first exposed port.
Your playwright.config must consume it:
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
},Without a wired service, no binding happens and your config's own webServer
(or hardcoded baseURL) is used as-is.
Browser APIs that require a secure
context
don't work against http://frontend:<port> — only localhost or HTTPS
origins qualify. Enable the localhost proxy to reach the wired service on a
secure-context origin:
[modules.playwright.settings]
service = "myapp:serve"
localhostProxy = trueThis proxies localhost:<port> to the service (via socat, installed with apt —
the default images qualify) and sets PLAYWRIGHT_LOCALHOST_BASE_URL for your
tests to use.
Configured under [modules.playwright.settings] in dagger.toml (or as flags
on dagger api call playwright):
sourcePath(default: discover): workspace path of the Playwright project. By default the module finds the directory containingplaywright.config.*; setting this is required when the workspace holds more than one Playwright project.service: module reference ("module:function") of the service under test.serviceHostname(defaultfrontend): hostname the service is bound as.baseImageAddress(default: derive): the image tests run in. By default it is derived from your project's@playwright/testversion (mcr.microsoft.com/playwright:v<version>-noble), so browsers always match your Playwright version. Derivation prefers the version installed perpackage-lock.json; without a lockfile it falls back to the version declared inpackage.json, so pin that exactly — a floating range like^1.58.2can install a newer Playwright than the derived image's browsers.baseCtr: a fullContaineroverride, also wireable (baseCtr = "base-images:chromium").npx playwrightmust work in it after dependency install.packageManager(defaultnpm): how project dependencies are installed (npm,yarn,pnpm,bun). yarn and pnpm are enabled via corepack.localhostProxy(defaultfalse): see secure contexts above.args(default[]): extra arguments for everyplaywright testinvocation, e.g.["--project", "chromium"].shards(default1): number of parallel shard containerstestruns — this is how you shard theplaywright:testcheck.
Two things to know about the test environment:
- The module sets
CI=true, so anything yourplaywright.configkeys offprocess.env.CI(workers, retries,forbidOnly) applies. In particular, aworkers: process.env.CI ? 1 : undefinedclamp serializes the whole suite — prefer a bounded value like4(the container is isolated, but unbounded workers can starve the browsers and blow test timeouts). - Branded browser channels (
msedge,chrome) are not present in the Playwright images — remove those projects or scope runs withargs.
[modules.playwright.settings]
shards = 4Shards run in parallel containers against the same wired service and fail fast
on the first failing shard. (report is single-run; shard report merging is
not yet supported.)
This repo is its own e2e fixture: the workspace wires
.dagger/modules/fixtures' static server into the module
(settings.service = "fixtures:server") and fixture/ holds a minimal
Playwright project, so dagger check exercises discovery, version derivation,
service wiring, and the localhost proxy end to end.