Conversation
Playwright projects become a collection keyed by config directory. The
constructor holds only settings; projects(ws) discovers every directory with a
playwright.config.{ts,js,mjs,cjs,mts,cts} at or below the cwd (plus the
enclosing one) with Workspace.findRoots, skipping node_modules and hidden
directories. Per-project logic (image derivation, install dir, service
binding, localhost proxy, sharding) moves onto PlaywrightProject; the batch
test runs the selected projects concurrently and reports every failure.
sourcePath is replaced by project(ws, path). Engine v1.0.0-beta.15 and the
dang-sdk collections branch are required. The e2e module gains a nested
fixture project and discovery, lookup, install and collection checks.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: kpenfound <[email protected]>
Drop the post-walk visibility filter: findRoots' excludes already prune node_modules and hidden directories from the walk. Add e2e cases for the three cwd scopes (project root, inside a project, outside any project), and update the README for cwd-aware selection, discovery rules and limits, the dev-engine requirement, and calling the collection from another module. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: kpenfound <[email protected]>
Resolve the dagger.lock conflict by keeping this branch's dang-sdk collections pin and main's sdk-helpers v1.0.5 resolution. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Kyle Penfound <[email protected]>
Each project's test files are a nested collection (PlaywrightTestFiles), keyed by project-relative path from one Workspace.search per project and filtered by the literal testDir, testMatch and testIgnore read statically from playwright.config (per Playwright project too; anything non-literal falls back to Playwright's defaults). Only the test-file level carries checks: with nothing filtered out the batch runs the project natively, sharded with --shard=i/N; a filtered run passes each file as an anchored, escaped absolute-path regex and splits the files into up to N groups, one container each, reporting every failing group. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: kpenfound <[email protected]>
Test-file discovery follows the config shapes found in the field:
export default <const> (TypeScript annotations included), one hop of a
relative import or re-export (export { config as default } from '../x.js'),
and a spread (defineConfig({ ...config, webServer })) whose keys the
config's own override. Template strings with ${} are masked whole. A
project where nothing is found still gets a '.' key that runs it whole.
Installs happen at the workspace root (pnpm-workspace.yaml / workspaces,
else lockfile, else package.json) with the package manager detected from
packageManager or the lockfile, corepack installed when missing, pnpm's
store passed with --store-dir, and only install inputs mounted (package.json
files, lockfiles, config, patches, file:/link:/injected dependency dirs and
workspace bin files) before the source is laid over. Tests run the
project's own node_modules/.bin/playwright. New settings: installFlags,
environment. Failures name the step (install vs playwright test) with the
end of its output.
The image version comes from pnpm-lock.yaml, yarn.lock, bun.lock or
package-lock.json, else the declaring package.json with catalog: resolved
from pnpm-workspace.yaml, and never yields an invalid reference. report
adds --reporter=list,html so the HTML report always exists.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: kpenfound <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: kpenfound <[email protected]>
Concurrent yarn 1 installs in one batch can corrupt a shared cache. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: kpenfound <[email protected]>
Batch failures now read "Playwright failed in …", since the cause may be the install. The environment example no longer uses a variable that picks a browser. The README explains that lifecycle scripts run before the source is mounted, and how to skip them with installFlags. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: kpenfound <[email protected]>
6 of 15 tasks
- setup: shell commands run after install with the full source, before the tests, e.g. building a workspace package the tests import. With a wired service there is no webServer to do it in. - args go after the module's own arguments: a variadic flag such as --project web consumed the file filters of a filtered run, and Playwright ignores filters after "--". - The localhost proxy listens dual-stack, so [::1] reaches the service as well as 127.0.0.1. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates the module to Dagger collections: Playwright projects keyed by config directory, each with a nested collection of test files.
Requires Dagger v1.0.0-beta.15 or later. The Dang SDK points at
github.com/dagger/dang-sdk@collections.Shape
Playwright(constructor): settings only (baseCtr,service,serviceHostname,baseImageAddress,packageManager,localhostProxy,args,shards). It no longer takes or stores theWorkspace.Playwright.projects(ws): PlaywrightProjects!(@collection):paths(@keys): root-relative directories that hold aplaywright.config.{ts,js,mjs,cjs,mts,cts}, from oneWorkspace.findRootswalk withnode_modulesand hidden directories pruned. Unique and sorted.project(path)(@get), andtest(ws), a plain function that runs the selected projects whole.Playwright.project(ws, path): the project containing a path. It replacessourcePath.PlaywrightProject:path,tests(ws),test(ws)(plain: a whole, sharded run),report(ws),base(ws),imageAddress(ws),installDir(ws).PlaywrightProject.tests(ws): PlaywrightTestFiles!(@collection):files(@keys): project-relative test file paths, unique and byte-sorted;delta: CollectionDelta,file(path)(@get);test(ws)(@check, the batch);runs: the containers the batch would start.PlaywrightTestFile:path,test(ws)(@check, "Run this test file.").PlaywrightRun:files,shard,label.Only the test-file level carries checks, which avoids the double-run trap.
dagger check -l --all -f=linkwith the module installed:The flags are
--playwright-project PATH,--playwright-projects,--playwright-test-file PATHand--playwright-tests. Checks are selected with--check test.baseandreportremain per-projectdagger shell/dagger list containers|directoriesartifacts. There are no stale or duplicate checks.Working-directory scoping
Project keys follow
findRootssemantics:So
cd apps/web/src && dagger checkruns onlyapps/web. Test-file keys are the same from any cwd.Test-file discovery (static, no containers)
Each project costs three cheap calls during listing: one
Workspace.globfor its config, oneWorkspace.fileread, and oneWorkspace.search(ripgrep, withnode_modulesand.gitpruned by the search). There are nowithExeccalls. The search's glob is Playwright's default pattern when everytestMatchis the default, and every script file otherwise. Results are then filtered in Dang. Files under a nested project's config belong to that project.Only literal config values are honoured:
testDir: a string, orpath.join/resolve(__dirname, '…'), resolved against the config directory.testMatch/testIgnore: a string glob, a regex literal, or an array of those.**/prepended,{a,b},@(),?(),+(),*())./app/<path>.projects: [{ … }], inheriting the top level. Keys are the union across Playwright projects.defineConfig({…}),export default {…}ormodule.exports = {…};export default <ident>naming aconstin the file, TypeScript annotation included (React Router's shape);export { config as default } from '../../utils.js';defineConfig({ ...config, webServer }), resolved the same way; the config's own keys override it (SvelteKit's shape).${}templates), package imports, more than one hop, regexes RE2 rejects (lookaround, backreferences),!()globs, and unreadable or unparseable configs.., which runs the whole project natively, so every project can be checked.Runs
--shard=i/N, N =shards).^<escaped absolute path>$, because Playwright treats positional args as case-insensitive regexes over absolute paths. I verified that anchored absolute paths select exactly one file and relative ones match nothing."$@", so the shell never re-parses them.Dependencies and image
This matches the other Node modules (prettier's implementation).
pnpm-workspace.yaml, or apackage.jsonwith"workspaces"), else the nearest lockfile, else the nearestpackage.json.packageManagersetting (default: detect), else the"packageManager"field, else the lockfile. corepack is installed if missing; pnpm gets--store-direxplicitly.package.jsonfiles, lockfiles, workspace and package-manager config,patches/,file:/link:/portal:and injected dependency directories, and workspacebinfiles. The rest of the source is laid over afterwards, so editing source leaves the installCACHED.node_modules/.bin/playwright, oryarn playwrightunder Plug'n'Play. It exits 127 with a message if missing;npxis used only when there is nopackage.jsonat all.installFlagsandenvironment(KEY=VALUE).install failed (pnpm install …, exit N):orplaywright test failed (exit N):, each followed by the end of the output.@playwright/testversion comes frompnpm-lock.yaml,yarn.lock,bun.lockorpackage-lock.json. Otherwise it comes from the nearestpackage.jsonthat declares it, withcatalog:andcatalog:<name>resolved frompnpm-workspace.yaml. Anything that isn't a version falls back to the pinned image, never to an invalid reference.reportruns with--reporter=list,htmlinto a fixed directory, so the HTML report always exists.CLI
Breaking changes
playwright/projects/tests/test, with--playwright-projectand--playwright-test-file. It replacesplaywright:test.PlaywrightProjects.testandPlaywrightProject.testare plain functions, not checks.sourcePathand the constructor'swsare removed.test,report,baseandimageAddressmove onto each project and takews.pathandinstallDirare root-relative.service = "dag://myapp/serve".packageManagernow defaults to detection, and installs happen at the workspace root.installDirreports that root.Tests
The e2e module (
dagger check, all passing on the released beta.15 engine):check-fixture: service binding, localhost proxy, 2 shards, via the test-file batch; asserts the whole run is1/2,2/2.discovery-check: project cwd scopes; test-file keys, identical from any cwd.config-check, one synthetic project per rule:testDir,path.join(__dirname, 'e2e');testMatchglob, array and regex, andtestIgnore;testDirunion;node_modulesand nested-project files are excluded;const config: PlaywrightTestConfig;.key.collection-check: both collections, coveringgetinside and outside a subset, subset order, unknown and duplicate keys rejected, and an empty subset (no runs).run-check, on a synthetic project with a failingaa.spec.ts:a.spec.tspasses, both via the batch and the item;[a,b]and[c];.-keyed project runs whole;reportreturnsindex.htmlwith a config that has no html reporter.image-check(static): install root, package manager and image for a pnpm catalog, a named catalog, a pnpm lockfile,yarn.lockwith workspaces, andworkspace:*falling back cleanly.install-run-check:file:dependency and a workspacebinused by the tests, andenvironmentapplied;installFlags: ["--ignore-scripts"]gets past it;@playwright/testfails clearly.file:fixture fails withCannot find module …/local-dep/index.js.I ran every README CLI example in a small scratch pnpm workspace on the released engine. I also checked caching there:
pnpm installisCACHED;reused 3, downloaded 0;Field-trial config shapes. I built a scratch copy with the trial repos' real config files and test file names (no dependencies). Listing now gives:
🤖 Generated with Claude Code