Skip to content

Commit a54664a

Browse files
authored
fix: wait_for_function with polling interval (#1586)
1 parent 66f7f3a commit a54664a

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

playwright/_impl/_frame.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,12 @@ async def wait_for_function(
746746
timeout: float = None,
747747
polling: Union[float, Literal["raf"]] = None,
748748
) -> JSHandle:
749+
if isinstance(polling, str) and polling != "raf":
750+
raise Error(f"Unknown polling option: {polling}")
749751
params = locals_to_params(locals())
750752
params["arg"] = serialize_argument(arg)
753+
if polling is not None and polling != "raf":
754+
params["pollingInterval"] = polling
751755
return from_channel(await self._channel.send("waitForFunction", params))
752756

753757
async def title(self) -> str:

tests/async/test_wait_for_function.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from datetime import datetime
16+
17+
import pytest
18+
19+
from playwright.async_api import Error, Page
20+
21+
22+
async def test_should_timeout(page: Page):
23+
start_time = datetime.now()
24+
timeout = 42
25+
await page.wait_for_timeout(timeout)
26+
assert ((datetime.now() - start_time).microseconds * 1000) >= timeout / 2
27+
28+
29+
async def test_should_accept_a_string(page: Page):
30+
watchdog = page.wait_for_function("window.__FOO === 1")
31+
await page.evaluate("window['__FOO'] = 1")
32+
await watchdog
33+
34+
35+
async def test_should_work_when_resolved_right_before_execution_context_disposal(
36+
page: Page,
37+
):
38+
await page.add_init_script("window['__RELOADED'] = true")
39+
await page.wait_for_function(
40+
"""() => {
41+
if (!window['__RELOADED'])
42+
window.location.reload();
43+
return true;
44+
}"""
45+
)
46+
47+
48+
async def test_should_poll_on_interval(page: Page):
49+
polling = 100
50+
time_delta = await page.wait_for_function(
51+
"""() => {
52+
if (!window['__startTime']) {
53+
window['__startTime'] = Date.now();
54+
return false;
55+
}
56+
return Date.now() - window['__startTime'];
57+
}""",
58+
polling=polling,
59+
)
60+
assert await time_delta.json_value() >= polling
61+
62+
63+
async def test_should_avoid_side_effects_after_timeout(page: Page):
64+
counter = 0
65+
66+
async def on_console(message):
67+
nonlocal counter
68+
counter += 1
69+
70+
page.on("console", on_console)
71+
with pytest.raises(Error) as exc_info:
72+
await page.wait_for_function(
73+
"""() => {
74+
window['counter'] = (window['counter'] || 0) + 1;
75+
console.log(window['counter']);
76+
}""",
77+
polling=1,
78+
timeout=1000,
79+
)
80+
81+
saved_counter = counter
82+
await page.wait_for_timeout(2000) # Give it some time to produce more logs.
83+
84+
assert "Timeout 1000ms exceeded" in exc_info.value.message
85+
assert counter == saved_counter
86+
87+
88+
async def test_should_throw_on_polling_mutation(page: Page):
89+
with pytest.raises(Error) as exc_info:
90+
await page.wait_for_function("() => true", polling="mutation")
91+
assert "Unknown polling option: mutation" in exc_info.value.message

0 commit comments

Comments
 (0)