Repository navigation
chore(frontend): upgrade Vitest, Playwright, jsdom, Prettier - #84
Merged
Merged
Conversation
Refs #35, E4 step 3. devDependencies only — Angular, PrimeNG and TypeScript stay where they are and wait for #31. @playwright/test 1.58.2 -> 1.63.0 jsdom 28.1.0 -> 30.1.0 prettier 3.8.1 -> 3.9.8 vitest 4.0.8 -> 5.0.1 One commit rather than one per tool: no changelog needed adapting. No config key changed, no spec changed, and the three things that looked like they might need work turned out not to. VITEST 5 IS OUTSIDE ANGULAR'S DECLARED PEER RANGE `@angular/build:unit-test` is what `ng test` drives, and it declares vitest as an optional peer at `^4.0.8` — on every 21.2.x and on 22.1.8, the newest published. So this pairing is unsupported upstream, not merely untested, and `npm install` prints `ERESOLVE overriding peer dependency`. Shipped anyway, on evidence rather than optimism: `npm ci` from a clean node_modules resolves it and exits 0, all 14 files and 198 tests pass, the test count is identical to vitest 4, and a deliberately broken assertion still fails one test — so the suite is running and biting, not passing for the wrong reason. The caveat is written into .claude/rules/testing.md next to the runner, because the peer warning in an install log is otherwise the kind of thing someone "fixes" by downgrading the wrong package. PRETTIER 3.9 REFORMATS NOTHING HERE The brief asked for the formatting change to be kept out of the diff if 3.9 moved anything. It does not: 3.8 and 3.9 report the same 30 files under `--list-different`, and the sets are identical. The repo is simply not Prettier-formatted and Prettier is not enforced anywhere, so there is no formatting commit and no .prettierrc pin. JSDOM 30 CHANGED NOTHING WE DEPEND ON `matchMedia`, `ResizeObserver` and `IntersectionObserver` are still undefined — checked twice, once against the library and once with a throwaway spec inside the running test environment, because those are not the same thing. The polyfill rule in .claude/rules/testing.md stands and now says it was verified against 30, so the next bump does not re-derive it. PLAYWRIGHT 1.63 Every config key still applies: 58 passed and 3 skipped, exercising webServer, baseURL, screenshot, trace and the chromium project. One full run of four had `router-leave-settings` fail; it passes 3 of 3 in isolation and 58/58 on the next two full runs. That is the flake class the config's own comment describes and `retries: 1` in CI exists for. Not attributed to 1.63 — establishing that would need the same repetition on 1.58, which was not done. Co-authored-by: Michael <0xMMA@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…t isolation Review round on #84. Two of my own claims in .claude/rules/testing.md were wrong, and the vitest 5 bump costs more than it buys. VITEST STAYS ON 4.1.11, NOT 5.0.1 The evidence I shipped it on was about the test RUN — clean `npm ci`, identical test count, a broken assertion still failing — and all of that holds. It missed the thing developers actually do: `npm install` fails outright. npm error code ERESOLVE npm error Found: vitest@5.0.1 ... peerOptional vitest@"^4.0.8" from @angular/build Not a warning, an error with a non-zero exit, for every package anyone tries to add, until they know to pass --legacy-peer-deps. CI runs `npm ci` everywhere, so CI would never have caught it — my green pipeline was not evidence about this. A test-runner major is not worth breaking local installs for, so the bump stops at 4.1.11: inside the declared peer range, `npm install --dry-run` clean, 198 tests green, and `clearMocks` still defaults to false as it did on 4.0.8. The conditions for moving to 5 are written down instead of lost: the builder has to declare `^5`, and the same change has to handle vitest 5 flipping `clearMocks` to true by default — the builder does not set it, so a mock's calls stop surviving into the next test and an assertion on call counts that should fail could start passing. SPEC FILES ARE NOT ISOLATED, AND I WROTE THE OPPOSITE I claimed each spec file gets its own environment. The builder sets `isolate: false`, so files in one fork share a process, a globalThis and one jsdom window. Measured: 11 of 14 files landed in a single fork and saw `ResizeObserver` and `matchMedia` already defined on entry, set by two files that ran before them. That also explains the leak I reported as "observed once, did not reproduce in ten runs" — it reproduces perfectly, it just depends on which fork a file lands in. The advice was right and the reason was wrong, which is the more damaging half: it told the next author that cross-file state cannot happen. The jsdom section is corrected the same way. `matchMedia`, `ResizeObserver` and `IntersectionObserver` are undefined in jsdom 30 and in a spec run on its own — but in a full run a spec may see them anyway, so a green suite is not proof that your stub is there. The rule now says to run a new or changed spec on its own once, with the command to do it. Co-authored-by: Michael <0xMMA@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 18, 2026
7 tasks
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.
Refs #35 — E4 step 3.
frontend/devDependencies only; Angular, PrimeNG and TypeScript stay put and wait for #31.@playwright/testjsdomprettiervitestVitest 5 was tried and rejected, on evidence
The brief asked for 4 → 5. It is not deliverable:
@angular/build:unit-testdeclares vitest as an optional peer at^4.0.8— on every published21.2.xand on22.1.8, the newest release.I shipped 5 anyway in the first version of this PR, on evidence that turned out to be about the wrong thing. The test run was fine — clean
npm ci, 198 tests, a deliberately broken assertion still failing, and the builder's watch/rerun path working. What it missed is what developers actually do:Not a warning — an error with a non-zero exit, for every package anyone tries to add, until they know to pass
--legacy-peer-deps. Every CI workflow usesnpm ci, which is unaffected, so a green pipeline was never evidence about this. A test-runner major is not worth breaking local installs for.So the bump stops at 4.1.11: inside the declared range,
npm install --dry-runclean, 198 tests green, andclearMocksstill defaults tofalseas on 4.0.8 — no silent semantic change.The conditions for moving to 5 are recorded in
.claude/rules/testing.mdrather than lost: the builder has to declare^5, and the same change has to handle vitest 5 flippingclearMockstotrueby default. The builder does not set it, so a mock's calls stop surviving into the next test — an assertion on call counts that should fail could start passing.Spec files are not isolated — and the rules said the opposite
The first version of this PR added a claim that each spec file gets its own environment. It is false. The builder sets
isolate: false, so files in one fork share a process, aglobalThisand one jsdom window.Measured: 11 of 14 files landed in a single fork and saw
ResizeObserverandmatchMediaalready defined on entry, set by two files that ran before them. Which fork a file lands in changes between runs.That also explains a leak I had reported as "observed once, did not reproduce in ten runs" — it reproduces perfectly; it depends on scheduling. The advice (every file sets its own stub) was right and the reason was wrong, which is the more damaging half: it told the next author that cross-file state cannot happen.
.claude/rules/testing.mdnow describes the mechanism and says to run a new or changed spec on its own once, with the command.All three load-bearing stubs were checked by removing them:
settings.component.spec.ts(56 tests fail),text-enhancement.component.spec.ts(36 fail),cli-probe-startup.spec.ts(3 fail). All 14 files also pass individually, so nothing in the suite currently depends on a leaked global.jsdom 30 changed nothing we depend on
matchMedia,ResizeObserverandIntersectionObserverare undefined in jsdom 30 — against the library and in a spec run on its own. In a full run a spec may see them anyway, per the section above; the rules file now says so, because "green suite" is not proof that your stub is there.Prettier 3.9 reformats nothing here
3.8 and 3.9 report the same files under
--list-different, and the diff of the two lists is empty. The repo is not Prettier-formatted and Prettier is not enforced anywhere — noformatscript, no CI step, no hook. No formatting commit, no.prettierrcpin.Playwright 1.63
Every config key still applies, and each was confirmed by its effect rather than by reading the types:
testDir(61 tests in 5 files, all frome2e/),projects([chromium]prefix),timeout(a hanging test reports "Test timeout of 30000ms exceeded"),retries(CI=1produces "(retry #1)" and a-retry1/artefact dir),reporter(list output +playwright-report/, no browser opened),use.baseURL,use.screenshot(attachment on failure),use.trace(trace.zip, only on failure), andwebServer.env(probe config whose server echoes the injected value).angular.json'stesttarget keys are all in@angular/build@21.2.0's schema.include: ["src/**/*.spec.ts"]is load-bearing: the schema default is["**/*.spec.ts", "**/*.test.ts"]relative to the project root, which would reach the Playwright specs ine2e/.58 passed, 3 skipped. The suite is flaky in one place —
router-leave-settingsfails in roughly one full run in three — and that is not from this bump: measured 3 failures in 7 runs on 1.63.0 against 1 in 4 on 1.58.2, same test, same cause. Filed with the one-line fix as #85.Also filed
locator.count()taken before the tabs render.playwright.config.tsinjectsNG_APP_ANTHROPIC_API_KEY, and nothing reads it; the comment describes an@ngx-env/builderfeature this project does not use.Both predate this PR.
Verification
npm install --dry-run— clean, exit 0 (the check the first version failed)npm cifrom a deletednode_modules— exit 0npm test— 14 files, 198 tests, 0 failures; all 14 files also pass individuallynpm run build— cleannpx playwright test— 58 passed, 3 skippednpx tsc -p tsconfig.spec.json --noEmit— exit 0git statusclean after the E2E runs🤖 Generated with Claude Code