fix(e2e): observe the dictation banner inside the 6s it exists - #733
Merged
Conversation
`a local-pinned agent does NOT fall back to the browser when the service is
down` failed intermittently with
Error: expect(locator).toContainText(expected) failed
Locator: locator('[data-testid="cn-ai-input-dictation-error"]')
Expected pattern: /private/i Timeout: 15000ms
Error: element(s) not found
The assertion outlived the thing it asserted on. CnAiInput's
`showDictationError()` arms `setTimeout(..., DICTATION_ERROR_TIMEOUT)` with
DICTATION_ERROR_TIMEOUT = 6000 -- the banner wipes itself after 6s,
deliberately, because "a banner with no lifetime becomes furniture". The test
allowed 15s to observe it. If the runner stalled between the click and the
first poll the banner had already cleared, and Playwright then waited out the
rest of the 15s for an element that no longer existed.
Measured on development 2026-08-31: 2 failures in 8 runs, and a `push` and a
`pull_request` run of the SAME commit disagreed -- the signature of a missed
window rather than of an app that failed to refuse.
So the observation now happens inside the banner's own lifetime, and the
trigger is repeated if the window was missed (bounded at 3). Re-clicking is
safe and changes nothing the test is about: the control is `aria-disabled`,
the app refuses the click either way, each call re-arms the banner's timer,
and the assertion that matters -- that NO browser recogniser was constructed
-- is checked over the whole test rather than per click.
Deliberately NOT a longer timeout and NOT a weakened matcher. The refusal
message is the point of the test: it still must appear, still must say
"private", and a genuine failure to show it still fails here after three
honest attempts.
Worst case: waitFor resolves at 4s leaving 2s of banner for the text
assertion; 3 attempts is ~12s, inside the spec's budget.
Contributor
Quality Report — ConductionNL/hermiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 123/123 | |||
| npm | ✅ | ✅ 730/730 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 17:39 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.
a local-pinned agent does NOT fall back to the browser when the service is downfailed intermittently with:The assertion outlived the thing it asserted on
CnAiInput'sshowDictationError()arms:The banner wipes itself after 6 seconds, deliberately — the component's own comment explains why: "a banner with no lifetime is a banner that becomes furniture".
The test allowed 15 seconds to observe it. If the runner stalled between the click and the first poll — plausible under CI contention — the banner had already cleared, and Playwright then waited out the rest of the 15s for an element that no longer existed. The message reads like a missing feature; the feature was there and gone.
Measured on development 2026-08-31: 2 failures in 8 runs, and a
pushrun and apull_requestrun of the same commit disagreed — the signature of a missed window, not of an app that failed to refuse.The fix
Observe inside the banner's own lifetime, and repeat the trigger if the window was missed (bounded at 3).
Re-clicking is safe and changes nothing the test is about: the control is
aria-disabled, the app refuses the click either way, each call re-arms the banner's timer, and the assertion that actually matters — that no browser recogniser was constructed — is checked over the whole test rather than per click.Deliberately not a longer timeout and not a weakened matcher. The refusal message is the point of this test: it still must appear, still must say "private", and a genuine failure to show it still fails here after three honest attempts. This test exists to prove confidential audio never reaches Google, and nothing about that guarantee is relaxed.
Timing
waitFor(visible)