Repository navigation
fix: give each retry attempt the configured timeout, not a fixed second - #276
joshdaugherty wants to merge 1 commit into
Conversation
waitForExpectation() ran every attempt under a hardcoded 1000ms, so Playwright::setTimeout() only set how long the loop kept retrying. An operation that fits the configured timeout but takes more than a second was cut off and run again: a slow script() ran several times, and a slow submit() submitted several times before timing out. Each attempt now gets what is left of the configured timeout, so such an operation finishes on its first attempt. Clamping to the remaining time keeps the loop inside the timeout, and retrying is unchanged, so an assertion still waits for text that appears later. submit() clicks the form's submit button, so it joins click() and the other actions that are not retried.
|
Independent reproduction of the remaining half at the current Environment: WSL2 (Linux 6.18 kernel in a VM, on a Windows host), PHP 8.4.24, Playwright 1.62.1, plugin The page records every entry and exit itself, so the count does not depend on reading the plugin: Route::get('/', fn (): string => '<span id="marker">ok</span>
<script>
window.log = [];
window.slow = function () {
window.log.push("enter@" + Math.round(performance.now()));
const until = performance.now() + 1500;
while (performance.now() < until) {}
window.log.push("leave@" + Math.round(performance.now()));
return "done";
};
</script>');
$page = visit('/');
$page->script('window.slow()'); // 1500ms of work, configured timeout 2000ms
$page->script('window.log.join(" | ")');Three consecutive runs, identical result each time: So the script ran three times, back to back, and the call still failed — with work that fits the configured timeout twice over. The failure is the part I had not expected from reading the code: the caller does not merely wait longer, it gets an exception for an operation that succeeded. A fourth run of the same test, the first one on a fresh checkout, ran it twice and returned One thing worth adding for anyone reading this after #269: the Happy to run anything else here if that helps. |
What happens
Execution::waitForExpectation()runs each attempt under a hardcodedPlaywright::usingTimeout(1_000, ...).Playwright::setTimeout()only decides how long the loop keeps retrying, and only the final call after the loop gets the configured value. So any page method still routed through the loop that takes more than a second is cut off and run again, even when it fits the configured timeout.#269 fixed this for
click()and the other actions by taking them out of the loop. Everything still in the loop keeps the fixed one-second attempt, andsubmit()is still in it, although it clicks the form's submit button.Measured on v5.0.0 with
setTimeout(5000), one call each, with a handler that blocks for 1.5 seconds and counts in the page how often it ran. Same result in each of 3 runs:click()(for comparison; #269 fixes it on5.x)submit()script()The fix
submit()joins the methods that are not retried, next toclick().This is narrower than #249, which also takes more actions out of the loop, and the two do not conflict.
Tests
SubmitTest: a submit handler that takes 1.5s runs once. A plain submit test is added too, sincesubmit()had none.ScriptTest: a script that takes 1.5s runs once. A plainscript()test is added too.AssertSeeTest: text that appears after 1.2s is still seen, so retrying still works.Against unchanged
5.xsource, the two "runs once" tests fail and the other new tests pass. Thesubmit()call itself fails there withTimeout 2000ms exceeded, after being retried.On this branch: the full suite passed 344 tests with 27 skipped. Three tests failed: two iframe tests and "it may visit external URLs". All three load external URLs and fail the same way on unchanged
5.xon this machine. PHPStan and type coverage (100%) pass, and Pint reports nothing on the changed files. Run on Windows 11 with PHP 8.4.Relates to pestphp/pest#1755.