pestphp / pestphp/pest

[pest-plugin-browser] A missing selector hangs forever instead of honouring the action timeout, and enforceTimeLimit then corrupts every later test in the process

Open
#1,908 0 comments 0 reactions 0 assignees View on GitHub

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-browser v4.3.1 (latest 4.x at the time of writing)
  • revolt/event-loop v1.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 with Must 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 defaultTimeLimit does not help — the alarm still fires while the fiber is
    suspended, just sooner.
  • Disabling enforceTimeLimit removes 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.