[pest-plugin-browser] A missing selector hangs forever instead of honouring the action timeout, and enforceTimeLimit then corrupts every later test in the process
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 11.7k
- Forks
- 538
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 8
Description
Filing here because issues are disabled on pestphp/pest-plugin-browser.
Summary
When a locator matches nothing, Locator::check() and Locator::fill() block indefinitely on
the Playwright websocket instead of failing at the action timeout. If PHPUnit's own time limit is
enabled, its pcntl alarm then fires while the fiber is suspended, which leaves Revolt's shared
DriverSuspension pending forever — so every subsequent test in the same process dies with
Must call resume() or throw() before calling suspend() again.
So one missing or unclickable element does not produce one failing test. It produces one aborted
test plus every remaining test in that process, with the real defect buried at the top of a wall of
unrelated errors.
Versions
pestphp/pest-plugin-browserv4.3.1 (latest 4.x at the time of writing)revolt/event-loopv1.0.9- PHP 8.4
Reproducer
Two tests. No application code and no component library — the only requirement is that the first
locator matches nothing on whatever page you visit.
<?php
it('a', fn () => visit('/')->fill('no_such_field_at_all', 'x'));
it('b', fn () => visit('/')->assertSee('anything that is really on the page'));
with the time limit enabled in phpunit.xml:
<phpunit enforceTimeLimit="true" defaultTimeLimit="120">
Observed
a— RISKY, aborted after 120 s. It does not fail at the browser action timeout.b— errors withMust call resume() or throw() before calling suspend() again, and so does
every test after it in that process.
Expected
a— fails at the action timeout with a "locator not found" style error, in about a second.b— unaffected.
What the trace shows
The check message is sent to Playwright with timeout:1000. One loadstate event comes back, and
then there is 119 seconds of silence — no reply, and nothing reaching the in-process amphp
server. The timeout is therefore transmitted, but the failure reply is either never delivered or
never processed, and the fiber is left suspended with nothing scheduled to resume it.
The pcntl alarm from enforceTimeLimit is what eventually interrupts it, and it does so at the
worst possible moment: mid-suspension, so the shared suspension is never resolved and the loop is
unusable for the remainder of the process.
Why the obvious workarounds do not help
- Lowering
defaultTimeLimitdoes not help — the alarm still fires while the fiber is
suspended, just sooner. - Disabling
enforceTimeLimitremoves the cascade and replaces it with a hang: with nothing to
interrupt the suspended fiber, the run never finishes.
There is no consumer-side configuration that turns this into one honest red test, which is why it is
reported rather than worked around.
Impact
We run ~627 assertNoJavaScriptErrors / assertNoConsoleLogs assertions in a single CI job. One
typo'd selector in an unrelated new test wipes all of them, so the job's green stops meaning
anything. Measured on one run: the first test went RISKY at 120 s and roughly 20 followed it down,
19 of which were noise.
Happy to test a patch against the reproducer above.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Locator::check() and Locator::fill(), then trace the Playwright websocket reply through the in-process amphp server and Revolt DriverSuspension. Run the two-test reproducer with enforceTimeLimit enabled in phpunit.xml; done means the missing locator fails at the action timeout and the following test remains unaffected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php, playwright
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100