Repository navigation
fix: await the matching navigation command response - #266
Conversation
|
Tried this against Applied on top of // from #247
if (isset($response['id']) && $response['id'] !== $requestId) {
continue;
}
…
yield $response;
// from this PR
if (isset($response['id']) && $response['id'] === $requestId) {
break;
}That is one rule stated twice from both ends — wait for your own response, skip what belongs to someone else — so the two PRs reinforce rather than overlap. Measured on PHP 8.4.24 / Playwright 1.62.1 / Chromium, Linux:
One caveat about the browser test in this PR, offered as data rather than criticism: For what it is worth, I also tried building a browser-level repro of the stranded-frame symptom from scratch (a navigation deliberately left unsettled at the end of a test, then a clean test after it) and could not make it fail on demand either. The unit-stub approach both of you took looks like the right call. |
|
Another independent reproduction on v5.1.2 (Chromium, Playwright 1.62, --parallel), with a deterministic test that needs no machine load: Route::get('/slow', function () { usleep(1_500_000); return 'slow'; });
$page = visit('/fast');
$page->navigate('/slow'); // /slow is served twice
$answers = array_map(fn ($i) => $page->script("() => {$i}"), range(1, 5));
// v5.1.2: [1, 1, 2, 3, 4]: every call reads the previous call's replyRed 3/3 on v5.1.2. With Heads-up: #277 (merged 2026-10-25) touched the same loop in Client::execute(), so #247 now conflicts with 5.x. |
|
Thanks |
A test can continue while its requested page is still loading.
Client::execute()currently treats any incoming load-state event matchingwaitUntilas completion of the navigation, even when that event belongs to another document or frame. This PR makes navigation wait for Playwright's response to the command that initiated it.How it works today
Page::goto()andPage::reload()send a command with a unique request ID andwaitUntil: 'load'. The WebSocket carries both command responses and asynchronous browser events. While reading that stream,Client::execute()stops on either:params.addmatcheswaitUntil.The second condition does not establish that the requested navigation completed. For example:
The next test action can therefore run against the initial blank document or during navigation. The real command response is also left unread; if navigation failed, its error is not reported by the navigation call that caused it.
We encountered this while expanding an application's browser suite to Firefox: accessibility checks saw
<html><head></head><body></body></html>, and other checks failed with an execution context destroyed during navigation. The deterministic unit tests below reproduce the underlying response-ordering bug independently of that application.How it works after this PR
Client::execute()continues consuming events until it receives the response with the current command's request ID. Playwright already implementswaitUntilfor the navigation command, so that response is the completion boundary:If the command returns an error after an unrelated load event, the existing error handling now reports it during the navigation call. The fix removes the event-based early exit; it adds no sleeps, extra readiness conditions or changes to the public browser API.
Regression coverage
gotoandreload) deliver initial-document and child-frame load events before the matching command response. Both prove that the client consumes the actual response.document.titleimmediately aftervisit()andnavigate(), without an intervening element assertion that could hide premature completion.All three unit cases fail before the fix and pass afterward. The browser regression passes in Chromium, Firefox and WebKit with Playwright 1.63.0. The full
composer testrun reports 344 passed and 27 platform skips; lint, type coverage and PHPStan pass. The existing PHPUnitcacheResultdeprecation is still reported.