test(e2e): fix the version-history flake — require rows inside the toPass() retry - #232
Merged
Merged
Conversation
…nt after it The two E2E runs of the SAME sha (6349aa6) disagreed on `version-rollback.spec.ts:314`: the pull_request run failed it in 36.6s, the push run passed it in 49.7s with 192/192. Identical code, so this is a race, not a defect — and the failing shape is the one this file already documents. `openVersionHistory()` is already an idempotent open-and-click under toPass(), and it works: it returns only once the panel body is on screen. What it could not cover was the caller's OWN assertion, made after it returned: await openVersionHistory(page, await appUuid(page)) await expect(page.locator('.version-history__row').first()).toBeVisible(...) The detail page re-mounts the sidebar after first paint — the helper's own comment block records this — and when that re-mount lands in the window between the helper returning and the caller asserting, there is no longer anything that can re-open it. The caller then polls a hidden element for 20s and reports the app hiding its rows. The CI log shows exactly that: the locator resolved to a real `<li class="version-history__row version-history__row--current">` on all 36 polls, `hidden` every time. So move the same assertion inside the loop via an opt-in `requireRows`, and drop the now-redundant external one at both call sites. Nothing is weakened: the condition asserted is identical, and for these two callers `.version-history__empty` correctly stops counting as success. The third call site keeps the either/or default — it asserts toHaveCount(), which counts DOM nodes regardless of visibility, which is why it never flaked. Type-checked with tsc --noEmit (exit 0). Positive control, because a clean tsc run and a zero-file run print the same nothing: the same file with `requireRows` misspelled fails TS2561, exit 2.
`quality / Frontend Check (format)` was the one red job on this PR. Fixed by
running the project's OWN command (`npm run format`, i.e. the quoted
`prettier --check "**/*.{js,ts,vue,css,scss}"`), not a hand-rolled prettier
invocation — an unquoted glob in an npm script silently narrows what is
checked, and this fleet has been bitten by that before.
The check named exactly one file, this one, and the fix is layout only: the
ternary assigning `panelBody` is reflowed onto its own lines. Verified from
the DIFF rather than the exit code, because an autofix that rewrites a string
literal is a semantic change wearing a formatting costume — on shillinq that
shape orphaned 17 translations across 36 locales by editing a `t()` key. Here
both selector literals are byte-identical, no `t()` is involved, and no
Playwright selector text moved.
Re-verified after the reflow: `npm run format` exit 0, `tsc --noEmit` exit 0.
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ❌ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-17 00:07 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| check-gitignore | ✅ | ||||
| check-nc-floor | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 626/626 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-17 00:45 UTC
Download the full PDF report from the workflow artifacts.
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.
Follow-up to #229 (merged). This commit was pushed to
fix/green-openbuildafter that PR merged, so it never reacheddevelopment. Rebuilt as a one-commit branch off currentorigin/development(7002e4f8).The finding: two runs of the same sha disagreed
version-rollback.spec.ts:314is non-deterministic, and I nearly filed it as a regression from my own change. What settled it was the paired run — every PR here produces both apushand apull_requestrun, i.e. two independent executions of identical code:Same commit, same suite, same 65 skips, opposite outcomes. The spec took 36.6 s on the run that failed and 49.7 s on the run that passed. It also passes on
developmentand passed at the previous sha. A single red run is one sample.Why it races, and why the existing hardening did not cover it
openVersionHistory()was already an idempotent open-and-click underexpect(...).toPass()— the right pattern, and it works: it returns only once the panel body is on screen. The gap was that the caller then re-asserted the same condition after it returned:The detail page re-mounts the sidebar after first paint (the helper's own comment block records this). When that re-mount lands in the window between the helper returning and the caller asserting, nothing can re-open it any more — the retry that exists for exactly this is one statement upstream. The failure then reads as the app hiding its own rows: the locator resolved to a real
<li class="version-history__row version-history__row--current">on all 36 polls of the 20 s wait,hiddenevery time.The change
Move the same assertion inside the retry via an opt-in
requireRows, and drop the now-redundant external one at both affected call sites..version-history__emptycorrectly stops counting as success.toHaveCount(3), andtoHaveCount()counts DOM nodes regardless of visibility — which is precisely why it has never flaked. Harmonising it with the other two would import the flake into the stable test.Weakening the assertion to silence the flake would have been the wrong trade; this does the opposite.
Verification, and its limits
tsc --noEmiton the edited spec: exit 0, no output.tscrun and a zero-file run print the same nothing: the same file withrequireRowsmisspelled fails TS2561, exit 2. So the check genuinely ran. (playwright test --listwould not have caught this — it is a parse check, not a type check.)Two caveats I am not glossing over:
check:manifesthas never run locally here — the worktree has nonode_modules, so the command errors on a missing module. That is "did not run", not a pass. CI is the first real measurement.