Skip to main content

Playwright

The Playwright module runs your Playwright browser tests in a container whose browsers always match your Playwright version, the same way locally, in CI, and in Dagger Cloud. Its distinctive feature is first-class module wiring. If another module in your workspace serves your app, one line of configuration points the tests at it. No glue module, no port juggling.

Add it to your workspace​

dagger module install dagger.io/js/playwright

Pin your project's @playwright/test version exactly (or commit a lockfile). The module runs your tests in the mcr.microsoft.com/playwright image matching the version in the lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lock). Without a lockfile it uses the version declared in package.json, where a floating range like ^1.58.2 is read as its lower bound and can install a newer Playwright than the image's browsers.

Run the check​

dagger check --playwright                                    # every Playwright project
dagger check --playwright-project=apps/web # one project
dagger check --playwright-project=apps/web --playwright-test-file=tests/login.spec.ts # one test file

The check is playwright/projects/tests/test. It installs your project's dependencies and runs the project's own playwright test once per project, over the selected test files. If your config declares a webServer, Playwright starts your app inside the container exactly as it does on your machine, with no further setup needed.

Projects and test files​

FlagCollectionKey
--playwright-projectprojectsDirectory holding the playwright.config, relative to the workspace root. The root is .
--playwright-test-filetests, on a projectTest file path, relative to the project

List the keys with dagger list playwright-projects -a and dagger list playwright-test-files -a. Select collection items describes how to select items.

A project is a directory holding playwright.config.ts, .js, .mjs, .cjs, .mts, or .cts. These are directories, not the browser projects inside a config. Projects are found at or below the directory dagger runs from, plus the nearest project that encloses it. Hidden directories and node_modules are left out.

Test files are found by reading the config's testDir, testMatch, and testIgnore when they are literal values, including those of each browser project, and Playwright's defaults otherwise. A project where no test file is found has the single key ., which runs the whole project.

Wire in the service under test​

If another module already serves your app, wire it into the tests instead of duplicating that knowledge in webServer. Two steps:

1. Set the service setting to a module reference. That is the install name of another module in your dagger.toml, and a function on it that returns a Service. Run dagger list services to list their DAG addresses:

[modules.playwright.settings]
service = "dag://myapp/serve"

2. Add PLAYWRIGHT_BASE_URL to your playwright.config. The module binds the service into the test container and communicates its address through this environment variable. If your config doesn't read it, your tests will ignore the wired service and keep targeting whatever baseURL hardcodes:

use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
},

Keep your local URL as the fallback so the same config works on your machine.

By default the service is bound as frontend; set serviceHostname if your tests need a different hostname.

Secure contexts (service workers, WebCrypto, PWA testing)​

Browser APIs that require a secure context don't work against http://frontend:<port>, since only localhost or HTTPS origins qualify. If any of your tests exercise service workers, WebCrypto, or other secure-context APIs, enable the localhost proxy:

[modules.playwright.settings]
service = "dag://myapp/serve"
localhostProxy = true

Then point those tests at PLAYWRIGHT_LOCALHOST_BASE_URL, which the proxy sets, for example as the baseURL of a dedicated project in your config:

{
name: 'chromium-pwa',
use: {
...devices['Desktop Chrome'],
baseURL: process.env.PLAYWRIGHT_LOCALHOST_BASE_URL || 'http://localhost:3000',
},
},

Prepare your config for the container​

The module sets CI=true, so review what your playwright.config keys off process.env.CI, such as retries, forbidOnly, and especially workers:

  • Replace a workers: process.env.CI ? 1 : undefined clamp with a bounded value like 4. The container is isolated, so the usual shared-CI reason to serialize doesn't apply, and a 1 clamp can make the suite many times slower. Don't go unbounded either, since too many workers starve the browsers and blow test timeouts.
  • Remove branded-browser projects or exclude them with the args setting. Those browsers (channel: 'msedge', channel: 'chrome') are not present in the Playwright images; the bundled chromium covers the same engine.

Configure it​

List the current settings with dagger module settings playwright, then change one with dagger module settings playwright <key> <value>. They live in dagger.toml under [modules.playwright.settings], and apply to every project. To test projects against different services, install the module twice under different names.

  • service is the DAG address ("dag://module/function") of the service under test.
  • serviceHostname (default frontend) is the hostname the service is bound as inside the test container.
  • baseImageAddress (default: derive) overrides the derived mcr.microsoft.com/playwright:v<version>-noble image.
  • baseCtr is a full Container override. It is also wireable, e.g. baseCtr = "dag://base-images/chromium".
  • packageManager (default empty) is the package manager that installs dependencies: npm, yarn, pnpm, or bun. Empty detects it from the packageManager field of package.json, then from the lockfile, then uses npm.
  • installFlags (default empty) are extra arguments for the install command, such as ["--ignore-scripts"].
  • setup (default empty) are shell commands run in the project directory after dependencies are installed and before the tests, such as ["pnpm --filter @my/lib build"] to build workspace packages the tests import.
  • environment (default empty) sets environment variables in the test containers, as KEY=VALUE.
  • localhostProxy (default false) enables the localhost proxy described under secure contexts above.
  • args (default []) are extra playwright test arguments, e.g. ["--project", "chromium"].
  • shards (default 1) splits each project's run across that many parallel containers against the same wired service. With every test file selected, each container runs one Playwright shard. With some selected, the selected files are split into up to that many groups. Every failing container is reported.
[modules.playwright.settings]
service = "dag://myapp/serve"
shards = 4

Get the HTML report​

To inspect a failing run, call report on a project. It runs the project's tests tolerating failures, with the html reporter added, and returns the HTML report directory. Export it to your machine:

dagger api call playwright project --path=apps/web report export --path=./playwright-report

project returns the project that contains a path.

Working with other modules​

Playwright covers browser-level end-to-end testing; pair it with Jest or Vitest for unit tests. Any module whose function returns a Service can be the app under test. That's the wiring contract, not a special integration.

source code