Repository navigation
fix: make the page waits send their request and wait for it - #273
Open
sebastiansulinski wants to merge 2 commits into
Open
sebastiansulinski wants to merge 2 commits into
sebastiansulinski wants to merge 2 commits into
Conversation
`Page::waitForFunction()`, `waitForLoadState()` and `waitForURL()` never waited: `Client::execute()` is a generator, and the three methods discarded its return value without iterating it, so the request was never even written to the socket and each call returned at once with its condition unchecked. `Webpage::waitForEvent()` and the internal callers in the console, screenshot, frame and tab helpers were no-ops for the same reason. `waitForFunction()` now goes through `sendMessage()` (the frame-level `Frame.waitForFunction` protocol method) and awaits the response, so it polls until the expression or function is truthy and fails with the action timeout otherwise. The wire protocol has no `waitForLoadState` or `waitForURL` method — both are client-side in Playwright's own bindings — so they are built on `waitForFunction()`: the load state is read from `document.readyState` (`domcontentloaded` once parsing has finished, `load` and `networkidle` once the load event has fired; network idleness cannot be observed by this synchronous client), and the URL is matched in the page with the same `*` wildcard `assertUrlIs()` accepts, against the path when the pattern starts with `/` and against `scheme://host[:port]/path` otherwise. The two names are dropped from the page-level operation list, where they no longer name a message. Tests cover a function becoming truthy, an expression becoming truthy, the argument, the timeout failure, the load states, `waitForEvent()`, a navigation to a path, a wildcard match and a URL that never matches; six of the ten fail against the previous implementation.
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.
What was wrong
Page::waitForFunction(),Page::waitForLoadState()andPage::waitForURL()never waited. Each calledClient::instance()->execute(...)and discarded the return value — butClient::execute()is a generator, so its body never ran: the request was never written to the WebSocket and the method returned at once with its condition unchecked.Webpage::waitForEvent()and the internal callers inMakesConsoleAssertions,MakesScreenshotAssertions,InteractsWithFramesandInteractsWithTabwere no-ops for the same reason.Reproduction on v4.3.1 and v5.0.1:
visit('/')->page()->waitForFunction('() => false')returns in 0 ms instead of failing at the action timeout. We hit it in an application suite whose tests "waited" on an editor's focus this way and passed by timing alone.The fix
waitForFunction()goes throughsendMessage()(the frame-levelFrame.waitForFunctionprotocol method) and awaits the response, so it polls until the expression or function returns a truthy value and fails with the action timeout otherwise. The argument is passed as before.waitForLoadStateorwaitForURLmethod (both are client-side in Playwright's own bindings:FrameWaitForFunctionParams,FrameWaitForSelectorParamsandFrameWaitForTimeoutParamsare the only frame waits inplaywright-core1.62.1's schema), so both are built onwaitForFunction():waitForLoadState()readsdocument.readyState:domcontentloadedonce parsing has finished,load(andnetworkidle, which this synchronous client cannot observe) once the load event has fired. The docblock says so.waitForURL()matches in the page with the same*wildcardassertUrlIs()accepts: against the path when the pattern starts with/, otherwise againstscheme://host[:port]/path, mirroringassertPathIs()andassertUrlIs().isPageLevelOperation(), where they no longer name a message.If you would rather keep this pull request to
waitForFunction()and take the other two separately, say so and I will split it.Tests
tests/Browser/Webpage/WaitForFunctionTest.php,WaitForLoadStateTest.php,WaitForEventTest.phpandWaitForURLTest.php: a function becoming truthy, an expression becoming truthy, the argument, the timeout failure, the three load states and the default,waitForEvent(), a navigation to a path, a wildcard match and a URL that never matches. Six of the ten fail against the previous implementation (allwaitForFunctionandwaitForURLcases); the load-state cases pass either way because a visited page has already loaded, so they cover the mapping and the fluent return only.composer testlocally on PHP 8.5.10 with Playwright 1.62.1: lint (rector, pint), profanity, type coverage 100 %, phpstan (level max, strict rules) and the unit and browser suites all pass.