Summary
Page::waitForLoadState() never sends anything. Client::execute() is declared : Generator and
its body yields, so calling it without iterating the returned generator runs none of its
body — the websocket message is never written. Page::waitForLoadState() discards the return
value, so the wait is a silent no-op.
src/Playwright/Page.php (current default branch, line ~237):
public function waitForLoadState(string $state = 'load'): self
{
Client::instance()->execute( // <- Generator, never iterated
$this->guid,
'waitForLoadState',
['state' => $state]
);
return $this;
}
Compare processVoidResponse() in Api/Concerns/InteractsWithPlaywright.php, which does
iterator_to_array($response) — that is what actually drives the generator. evaluate() escapes
the bug only because processResultResponse() happens to foreach it.
Falsification
An invalid load state passes, which a real waitForLoadState could not do:
it('proves waitForEvent never reaches the server', function () {
visit('/')->waitForEvent('banana'); // passes
});
'banana' is not a valid Playwright load state. If the message were sent, the server would
reject it. It passes because nothing is sent.
Impact
This is quiet in the worst way. waitForEvent('networkidle') reads like a guard against a
navigation race and provides none, so tests that look synchronised are not, and the resulting
failures point at whatever assertion happened to read the previous document — not at the wait.
It is worse on CI, where the race is likelier to be lost.
Suggested fix
Consume the generator, as the other call sites do:
foreach (Client::instance()->execute($this->guid, 'waitForLoadState', ['state' => $state]) as $_) {
// drive the generator
}
A : Generator return type that is only correct when iterated is easy to misuse. It may be worth
auditing for other discarded execute() calls, and/or having execute() return a value rather
than a generator for the void cases.
Environment
pest-plugin-browser v4.3.1 (verified still present on the default branch and in v5.0.1), Pest
4.7.8, PHP 8.4, playwright 1.62.1, macOS.
Related: #1835 (the metadata.timeout protocol drift — different defect, same file, already
fixed).
Summary
Page::waitForLoadState()never sends anything.Client::execute()is declared: Generatorandits body
yields, so calling it without iterating the returned generator runs none of itsbody — the websocket message is never written.
Page::waitForLoadState()discards the returnvalue, so the wait is a silent no-op.
src/Playwright/Page.php(current default branch, line ~237):Compare
processVoidResponse()inApi/Concerns/InteractsWithPlaywright.php, which doesiterator_to_array($response)— that is what actually drives the generator.evaluate()escapesthe bug only because
processResultResponse()happens toforeachit.Falsification
An invalid load state passes, which a real
waitForLoadStatecould not do:'banana'is not a valid Playwright load state. If the message were sent, the server wouldreject it. It passes because nothing is sent.
Impact
This is quiet in the worst way.
waitForEvent('networkidle')reads like a guard against anavigation race and provides none, so tests that look synchronised are not, and the resulting
failures point at whatever assertion happened to read the previous document — not at the wait.
It is worse on CI, where the race is likelier to be lost.
Suggested fix
Consume the generator, as the other call sites do:
A
: Generatorreturn type that is only correct when iterated is easy to misuse. It may be worthauditing for other discarded
execute()calls, and/or havingexecute()return a value ratherthan a generator for the void cases.
Environment
pest-plugin-browser v4.3.1 (verified still present on the default branch and in v5.0.1), Pest
4.7.8, PHP 8.4, playwright 1.62.1, macOS.
Related: #1835 (the
metadata.timeoutprotocol drift — different defect, same file, alreadyfixed).