-
Notifications
You must be signed in to change notification settings - Fork 4
chore(e2e): run Playwright visual tests in Docker (#DS-5311) #1845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # Build context for tools/e2e/Dockerfile, whose build context is the repository root. | ||
| # | ||
| # It lives here rather than beside the Dockerfile because BuildKit only reads | ||
| # `<context>/.dockerignore` or `<dockerfile>.dockerignore`, and this is the location that works on | ||
| # every toolchain. A plain `.dockerignore` next to the Dockerfile is read by nothing and fails | ||
| # silently, shipping the entire working tree. | ||
| # | ||
| # Deliberately a denylist. An allowlist omits files silently: forgetting `tools/builders/` breaks | ||
| # `yarn install`'s postinstall, and forgetting `packages/e2e/utils/` breaks deep inside `ng serve` | ||
| # with an error that points nowhere near the cause. | ||
| # | ||
| # NOTE: these patterns are not recursive by default. A bare `node_modules` would match only the | ||
| # repository root and miss `.opencode/node_modules` (56 MB, self-ignored so it never appears in | ||
| # `git status`) — hence the `**/` prefixes. | ||
| # | ||
| # Verify with: | ||
| # docker build --progress=plain --no-cache -f tools/e2e/Dockerfile \ | ||
| # --build-arg PLAYWRIGHT_VERSION=<version> . 2>&1 | grep "transferring context" | ||
| # Expect roughly 57 MB. Substantially more means one of the patterns below stopped matching. | ||
|
|
||
| **/node_modules | ||
| **/dist | ||
| **/*.log | ||
|
|
||
| .git | ||
| .angular | ||
| .nx | ||
| .yarn/cache | ||
| .yarn/install-state.gz | ||
| .yarn/unplugged | ||
|
|
||
| # Playwright outputs. The container produces its own; the committed __screenshots__ baselines it | ||
| # compares against are deliberately not excluded. | ||
| blob-report | ||
| playwright-report | ||
| playwright-report-docs | ||
| test-results | ||
| .playwright-mcp | ||
|
|
||
| # Editor, agent and local tooling state. | ||
| .ai | ||
| .claude | ||
| .idea | ||
| .opencode | ||
| .vscode | ||
| coverage | ||
| tmp |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Deliberately narrow. `* text=auto` is tempting but would renormalize every tracked file in a | ||
| # single commit, so line endings are left alone except where they are load-bearing. | ||
|
|
||
| # The Docker build inputs are consumed by a Linux shell. A contributor with core.autocrlf=true | ||
| # would otherwise commit CRLF into the Dockerfile's `RUN` continuations and the yarn shim it | ||
| # writes. | ||
| tools/e2e/** text eol=lf | ||
|
|
||
| # Git already detects these as binary; declaring it means no future filter or `text=auto` change | ||
| # can start mangling the screenshot baselines, which are compared byte-for-byte at threshold: 0. | ||
| *.png binary |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,8 +91,17 @@ yarn run e2e:setup # Install Playwright browsers (run once) | |
| yarn run e2e:components # Run all component E2E tests | ||
| yarn run e2e:docs # Run the docs site smoke suite (needs `yarn run docs:build` first) | ||
| npx playwright test <TEST_PATH_PATTERN> # Run specific E2E tests (e.g., npx playwright test packages/components/button/e2e.playwright-spec.ts) | ||
|
|
||
| # Screenshots differ across operating systems — always use Docker for anything visual: | ||
| yarn run e2e:docker # Run E2E tests in Docker (matches CI) | ||
| yarn run e2e:docker:update-snapshots # Run E2E tests in Docker and update the baselines | ||
| ``` | ||
|
|
||
| The committed baselines under `__screenshots__` are compared with `threshold: 0` and have no | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. это не нужно в AGENTS.md, дубль из packages/e2e/README.md
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Одно для человека другое для агента, так же ? и тут скорее из ридми нужно выбрасывать и для агента больше писать.. |
||
| platform suffix, so a native run outside Linux fails on font rasterization alone. `e2e:components` | ||
| is still useful for the assertion-based specs; use `e2e:docker` whenever screenshots are involved, | ||
| and never regenerate a baseline any other way. | ||
|
|
||
| ### Linting | ||
|
|
||
| ```bash | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,49 @@ const viewport: ViewportSize = { | |
| const baseURL = process.env.BASE_URL || 'http://localhost:4200'; | ||
| const webServerCommand = process.env.WEB_SERVER_COMMAND || 'yarn run dev:e2e --configuration=production'; | ||
|
|
||
| /** | ||
| * Every worker drives its own browser against one shared Angular dev server, so the useful ceiling | ||
| * comes from that server rather than from the core count. '100%' suits a 4-vCPU CI runner, but not | ||
| * Docker: a container reports every core on the host (Playwright reads `os.cpus()`, which no cgroup | ||
| * or cpuset limit affects), so on a 32-core machine it means 64 browsers and the suite collapses | ||
| * into timeouts. tools/e2e's compose file caps it via PLAYWRIGHT_WORKERS and CI sets it back. | ||
| * | ||
| * Playwright only accepts a string when it is a percentage, so anything else has to become a number. | ||
| * With the variable unset this behaves exactly as it did before. | ||
| * | ||
| * The value is validated rather than passed through, because Playwright's own guard only rejects | ||
| * `workers <= 0` — and `NaN <= 0` is false. A typo like `PLAYWRIGHT_WORKERS=amx` would therefore | ||
| * reach the dispatcher's `for (i = 0; i < workers; i++)` loop, spawn zero workers, run zero tests, | ||
| * write no report, and still exit 0: a green suite that tested nothing. | ||
| */ | ||
| const resolveWorkers = () => { | ||
| const override = process.env.PLAYWRIGHT_WORKERS?.trim(); | ||
|
|
||
| if (!override) { | ||
| return isCI ? '100%' : undefined; | ||
| } | ||
|
|
||
| if (override.endsWith('%')) { | ||
| const percentage = Number(override.slice(0, -1)); | ||
|
|
||
| if (!Number.isFinite(percentage) || percentage <= 0) { | ||
| throw new Error(`PLAYWRIGHT_WORKERS must be a positive percentage, got ${JSON.stringify(override)}.`); | ||
| } | ||
|
|
||
| return override; | ||
| } | ||
|
|
||
| const workers = Number(override); | ||
|
|
||
| if (!Number.isInteger(workers) || workers <= 0) { | ||
| throw new Error( | ||
| `PLAYWRIGHT_WORKERS must be a positive integer or a percentage, got ${JSON.stringify(override)}.` | ||
| ); | ||
| } | ||
|
|
||
| return workers; | ||
| }; | ||
|
|
||
| /** @see https://playwright.dev/docs/test-configuration */ | ||
| export default defineConfig({ | ||
| testDir: __dirname, | ||
|
|
@@ -17,7 +60,7 @@ export default defineConfig({ | |
| fullyParallel: true, | ||
| forbidOnly: isCI, | ||
| retries: isCI ? 2 : 0, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. в идеале конечно отключить retries в 0, чтобы исключить нестабильные тесты на этапе разработки, но лучше это отдельно сделать |
||
| workers: isCI ? '100%' : undefined, | ||
| workers: resolveWorkers(), | ||
| reporter: [ | ||
| ['list', { printSteps: true }], | ||
| ['html', { open: 'never' }] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
удобный подход, когда ты игнорируешь все по умолчанию, и кладешь в образ только то, что действительно необходимо, пример:
я к тому что, в данной реализации в докер образ попадают например packages/components-dev, packages/docs-examples и тд
сам файл можно положить рядом с Dockerfile в tools/e2e/