Skip to content

Commit df2bf0b

Browse files
chore: roll Playwright to 1.66.0-next (0ea3691032) (#3318)
Co-authored-by: microsoft-playwright-automation[bot] <203992400+microsoft-playwright-automation[bot]@users.noreply.github.com>
1 parent 989329c commit df2bf0b

38 files changed

Lines changed: 478 additions & 816 deletions

‎DRIVER_VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
eac236da288cdc3d0891278a61e1a876348950a0
1+
0ea36910324ed8aa2a5f460317cbd35881ee88d4

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
66
| :--- | :---: | :---: | :---: |
77
| Chromium <!-- GEN:chromium-version -->158.0.8127.2<!-- GEN:stop --> | ✅ | ✅ | ✅ |
88
| WebKit <!-- GEN:webkit-version -->27.2<!-- GEN:stop --> | ✅ | ✅ | ✅ |
9-
| Firefox <!-- GEN:firefox-version -->158.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
9+
| Firefox <!-- GEN:firefox-version -->159.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |
1010

1111
## Documentation
1212

‎playwright/_impl/browser_context.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,8 +387,8 @@ def _on_request_finished(
387387
async def _on_route(self, route: network.Route) -> None:
388388
route._context = self
389389
page = route.request._safe_page()
390-
route_handlers = self._routes[:]
391-
for route_handler in route_handlers:
390+
route_handlers = self._routes
391+
for route_handler in route_handlers[:]:
392392
# If the page or the context was closed we stall all requests right away.
393393
if (page and page._close_was_called) or self.is_closed():
394394
return
@@ -404,7 +404,8 @@ async def _on_route(self, route: network.Route) -> None:
404404
if route_handler.will_expire():
405405
del self._routes[index]
406406
handled = await route_handler.handle(route)
407-
if not self._routes:
407+
# unrouteAll() replaces _routes and updates interception after waiting for handlers.
408+
if route_handlers is self._routes and not self._routes:
408409
fire_and_forget(self._disable_interception())
409410
if handled:
410411
return

‎playwright/_impl/event_emitter.py‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ def emit(self, type: EventType, *args: Any) -> bool:
8282
return True
8383

8484
def _call_handler(self, type: EventType, handler: Listener, args: Any) -> None:
85-
promise = handler(*args)
86-
if not inspect.isawaitable(promise):
85+
try:
86+
promise = handler(*args)
87+
if not inspect.isawaitable(promise):
88+
return
89+
# Python: an awaitable is the counterpart of a JS promise or thenable.
90+
future = asyncio.ensure_future(promise)
91+
except Exception as error:
92+
self._handle_listener_error(type, error)
8793
return
88-
# Python: the coroutine of an async def handler runs as a task.
89-
future = asyncio.ensure_future(promise)
9094
pending = self._pending_handlers.get(type)
9195
if pending is None:
9296
pending = set()
@@ -103,11 +107,19 @@ def on_done(f: "asyncio.Future[Any]") -> None:
103107
if self._rejection_handler:
104108
self._rejection_handler(e)
105109
else:
106-
# Python: the counterpart of an unhandled rejection.
107-
self._report_unhandled_error(e)
110+
try:
111+
self._handle_listener_error(type, e)
112+
except Exception as error:
113+
# Python: report an unhandled rejection outside the task callback.
114+
self._report_unhandled_error(error)
108115

109116
future.add_done_callback(on_done)
110117

118+
def _handle_listener_error(self, type: EventType, error: BaseException) -> None:
119+
if type == "error" or not self.listener_count("error"):
120+
raise error
121+
self.emit("error", error)
122+
111123
def _report_unhandled_error(self, error: BaseException) -> None:
112124
# Python: an unhandled rejection crashes Node.js; see report_unhandled_error().
113125
report_unhandled_error(error)

‎playwright/_impl/page.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ def _on_frame_detached(self, frame: Frame) -> None:
330330

331331
async def _on_route(self, route: Route) -> None:
332332
route._context = self.context
333-
route_handlers = list(self._routes)
334-
for route_handler in route_handlers:
333+
route_handlers = self._routes
334+
for route_handler in route_handlers[:]:
335335
# If the page was closed we stall all requests right away.
336336
if self._close_was_called or self._browser_context.is_closed():
337337
return
@@ -346,7 +346,8 @@ async def _on_route(self, route: Route) -> None:
346346
if route_handler.will_expire():
347347
self._routes.pop(index)
348348
handled = await route_handler.handle(route)
349-
if not self._routes:
349+
# unrouteAll() replaces _routes and updates interception after waiting for handlers.
350+
if route_handlers is self._routes and not self._routes:
350351
fire_and_forget(self._disable_interception())
351352
if handled:
352353
return

‎playwright/async_api/_generated.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4009,6 +4009,10 @@ async def connect_over_cdp(
40094009
endpoint_url : str
40104010
A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or
40114011
`ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`.
4012+
4013+
Alternatively, a browser channel name such as `chrome` or `msedge` to connect to a browser with remote debugging
4014+
enabled in its default user data directory. On Linux, Playwright uses `CHROME_USER_DATA_DIR` when set. Otherwise,
4015+
it uses `CHROME_CONFIG_HOME`, then `XDG_CONFIG_HOME`, then `~/.config` to locate that directory.
40124016
artifacts_dir : Union[pathlib.Path, str, None]
40134017
If specified, browser artifacts (such as traces and downloads) are saved into this directory.
40144018
headers : Union[Dict[str, str], None]
@@ -4018,10 +4022,10 @@ async def connect_over_cdp(
40184022
upon the file system being the same between Playwright and the Browser.
40194023
no_defaults : Union[bool, None]
40204024
When true, Playwright will not apply its default overrides to the existing default browser context. Specifically,
4021-
`accept_downloads` is left at the browser's setting, focus emulation is not enabled, and media emulation options
4022-
(such as `color_scheme`, `reduced_motion`, `forced_colors`, and `contrast`) are not applied. Useful when attaching
4023-
to a user's daily-driver browser where these overrides would interfere with existing browser state. New contexts
4024-
created via `browser.new_context()` are not affected. Defaults to `false`.
4025+
`accept_downloads` and font families are left at the browser's settings, focus emulation is not enabled, and media
4026+
emulation options (such as `color_scheme`, `reduced_motion`, `forced_colors`, and `contrast`) are not applied.
4027+
Useful when attaching to a user's daily-driver browser where these overrides would interfere with existing browser
4028+
state. New contexts created via `browser.new_context()` are not affected. Defaults to `false`.
40254029
slow_mo : Union[float, None]
40264030
Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going
40274031
on. Defaults to 0.
@@ -10735,7 +10739,9 @@ async def aria_snapshot(
1073510739

1073610740
An AI-optimized snapshot, controlled by `mode`, is different from a default snapshot:
1073710741
1. Includes element references `[ref=e2]`. 2. Does not wait for an element matching the locator, and throws when
10738-
no elements match. 3. Includes snapshots of `<iframe>`s inside the target.
10742+
no elements match. 3. Includes snapshots of `<iframe>`s inside the target. 4. Can omit names that repeat
10743+
printed descendants, such as a heading containing a link. Names that identify dialogs, landmarks, groups,
10744+
tables and table headers are retained.
1073910745

1074010746
Parameters
1074110747
----------

‎playwright/sync_api/_generated.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,6 +3935,10 @@ def connect_over_cdp(
39353935
endpoint_url : str
39363936
A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or
39373937
`ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`.
3938+
3939+
Alternatively, a browser channel name such as `chrome` or `msedge` to connect to a browser with remote debugging
3940+
enabled in its default user data directory. On Linux, Playwright uses `CHROME_USER_DATA_DIR` when set. Otherwise,
3941+
it uses `CHROME_CONFIG_HOME`, then `XDG_CONFIG_HOME`, then `~/.config` to locate that directory.
39383942
artifacts_dir : Union[pathlib.Path, str, None]
39393943
If specified, browser artifacts (such as traces and downloads) are saved into this directory.
39403944
headers : Union[Dict[str, str], None]
@@ -3944,10 +3948,10 @@ def connect_over_cdp(
39443948
upon the file system being the same between Playwright and the Browser.
39453949
no_defaults : Union[bool, None]
39463950
When true, Playwright will not apply its default overrides to the existing default browser context. Specifically,
3947-
`accept_downloads` is left at the browser's setting, focus emulation is not enabled, and media emulation options
3948-
(such as `color_scheme`, `reduced_motion`, `forced_colors`, and `contrast`) are not applied. Useful when attaching
3949-
to a user's daily-driver browser where these overrides would interfere with existing browser state. New contexts
3950-
created via `browser.new_context()` are not affected. Defaults to `false`.
3951+
`accept_downloads` and font families are left at the browser's settings, focus emulation is not enabled, and media
3952+
emulation options (such as `color_scheme`, `reduced_motion`, `forced_colors`, and `contrast`) are not applied.
3953+
Useful when attaching to a user's daily-driver browser where these overrides would interfere with existing browser
3954+
state. New contexts created via `browser.new_context()` are not affected. Defaults to `false`.
39513955
slow_mo : Union[float, None]
39523956
Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going
39533957
on. Defaults to 0.
@@ -10733,7 +10737,9 @@ def aria_snapshot(
1073310737

1073410738
An AI-optimized snapshot, controlled by `mode`, is different from a default snapshot:
1073510739
1. Includes element references `[ref=e2]`. 2. Does not wait for an element matching the locator, and throws when
10736-
no elements match. 3. Includes snapshots of `<iframe>`s inside the target.
10740+
no elements match. 3. Includes snapshots of `<iframe>`s inside the target. 4. Can omit names that repeat
10741+
printed descendants, such as a heading containing a link. Names that identify dialogs, landmarks, groups,
10742+
tables and table headers are retained.
1073710743

1073810744
Parameters
1073910745
----------

‎tests/async/library/chromium/test_connect_over_cdp.py‎

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
import json
1919
import os
2020
import signal
21+
import sys
2122
import urllib.request
2223
from contextlib import suppress
2324
from pathlib import Path
24-
from typing import Any, Dict, Generator, List
25+
from typing import Any, Dict, Generator, List, Optional
2526

2627
import pytest
2728

@@ -35,7 +36,7 @@
3536
expect,
3637
)
3738
from tests.helpers.proxy_server import ProxyServer
38-
from tests.helpers.remote_server import RemoteServer
39+
from tests.helpers.remote_server import ChildProcess, RemoteServer
3940
from tests.server import Server, TestServerRequest, WebSocketProtocol, find_free_port
4041

4142
pytestmark = pytest.mark.only_browser("chromium")
@@ -83,16 +84,66 @@ async def start_playwright_with_env(
8384

8485

8586
async def test_CD_001_should_connect_to_an_existing_cdp_session(
86-
browser_type: BrowserType, launch_arguments: Dict, port: int
87+
browser_type: BrowserType,
88+
launch_arguments: Dict,
89+
tmp_path: Path,
90+
is_linux: bool,
8791
) -> None:
88-
browser_server = await launch(browser_type, launch_arguments, port)
92+
config_home = tmp_path / "config"
93+
user_data_dir = config_home / "google-chrome"
94+
browser_server = await browser_type.launch_persistent_context(
95+
user_data_dir, **launch_arguments, args=["--remote-debugging-port=0"]
96+
)
8997
try:
98+
port = (user_data_dir / "DevToolsActivePort").read_text().splitlines()[0]
9099
cdp_browser = await browser_type.connect_over_cdp(
91100
endpoint_url=f"http://127.0.0.1:{port}/",
92101
)
93102
contexts = cdp_browser.contexts
94103
assert len(contexts) == 1
95104
await cdp_browser.close()
105+
106+
if is_linux:
107+
environments: List[Dict[str, Optional[str]]] = [
108+
{
109+
"CHROME_USER_DATA_DIR": "",
110+
"CHROME_CONFIG_HOME": "",
111+
"XDG_CONFIG_HOME": str(config_home),
112+
},
113+
{
114+
"CHROME_USER_DATA_DIR": "",
115+
"CHROME_CONFIG_HOME": str(config_home),
116+
"XDG_CONFIG_HOME": str(tmp_path / "unused-config"),
117+
},
118+
{
119+
"CHROME_USER_DATA_DIR": str(user_data_dir),
120+
"CHROME_CONFIG_HOME": str(tmp_path / "unused-config"),
121+
"XDG_CONFIG_HOME": str(tmp_path / "unused-xdg-config"),
122+
},
123+
]
124+
for env in environments:
125+
# Python: use the Python client in the child process.
126+
child = ChildProcess(
127+
[
128+
sys.executable,
129+
"-c",
130+
"""
131+
import asyncio
132+
from playwright.async_api import async_playwright
133+
134+
async def main():
135+
async with async_playwright() as playwright:
136+
browser = await playwright.chromium.connect_over_cdp('chrome')
137+
print('contexts: ' + str(len(browser.contexts)))
138+
await browser.close()
139+
140+
asyncio.run(main())
141+
""",
142+
],
143+
env=env,
144+
)
145+
assert await asyncio.to_thread(child.exit_code) == 0, child.output
146+
assert "contexts: 1" in child.output
96147
finally:
97148
await browser_server.close()
98149

‎tests/async/library/test_page_clock.py‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,15 +663,26 @@ async def test_CK_044_while_on_pause_run_for_should_not_run_nested_immediate_fro
663663
async def test_CK_045_while_on_pause_should_stay_paused_on_the_next_page_when_an_init_script_reads_the_time(
664664
page: Page, server: Server
665665
) -> None:
666+
def handler(request: TestServerRequest) -> None:
667+
request.setHeader("Content-Type", "text/html")
668+
request.write(
669+
b"<script>setTimeout(() => document.documentElement.dataset.fired = 'true', 0);</script>"
670+
)
671+
request.finish()
672+
673+
server.set_route("/timer.html", handler)
666674
await page.clock.install(time=0)
667675
await page.clock.pause_at(1)
668676
await page.add_init_script("(() => Date.now())()")
669677
await page.goto(server.EMPTY_PAGE)
670678
await page.clock.run_for(1000)
671-
await page.goto(server.PREFIX + "/title.html")
679+
await page.goto(server.PREFIX + "/timer.html")
672680
assert await page.evaluate("() => Date.now()") == 2000
673681
await page.wait_for_timeout(500)
674682
assert await page.evaluate("() => Date.now()") == 2000
683+
assert await page.evaluate("() => document.documentElement.dataset.fired") is None
684+
await page.clock.run_for(0)
685+
assert await page.evaluate("() => document.documentElement.dataset.fired") == "true"
675686

676687

677688
async def test_CK_046_while_on_pause_should_not_advance_performance_now_before_the_first_clock_call_on_a_new_page(
@@ -683,6 +694,26 @@ async def test_CK_046_while_on_pause_should_not_advance_performance_now_before_t
683694
assert await page.evaluate("() => [performance.now(), Date.now()]") == [0, 1000]
684695

685696

697+
async def test_CK_220_while_on_pause_should_keep_timers_scheduled_during_load_pending_when_paused(
698+
page: Page, server: Server
699+
) -> None:
700+
def handler(request: TestServerRequest) -> None:
701+
request.setHeader("Content-Type", "text/html")
702+
request.write(
703+
b"<script>setTimeout(() => document.documentElement.dataset.fired = 'true', 0);</script>"
704+
)
705+
request.finish()
706+
707+
server.set_route("/timer.html", handler)
708+
await page.clock.install(time=0)
709+
await page.clock.pause_at(1)
710+
await page.goto(server.PREFIX + "/timer.html")
711+
await page.wait_for_timeout(500)
712+
assert await page.evaluate("() => document.documentElement.dataset.fired") is None
713+
await page.clock.run_for(0)
714+
assert await page.evaluate("() => document.documentElement.dataset.fired") == "true"
715+
716+
686717
async def test_CK_047_date_now_check_date_now_is_an_integer(page: Page) -> None:
687718
await page.clock.install()
688719
await page.goto("data:text/html,")

‎tests/async/library/test_proxy.py‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import pytest
2323

24-
from playwright.async_api import BrowserType, Error
24+
from playwright.async_api import BrowserType, Error, Request
2525
from tests.helpers.proxy_server import ProxyRequest, ProxyServer
2626
from tests.helpers.socks_server import MockSocksServer, SocksForwardingServer
2727
from tests.server import Server, TestServerRequest
@@ -71,8 +71,18 @@ async def test_PR_002_should_use_proxy_smoke(
7171
**launch_arguments, proxy={"server": server.HOST}
7272
)
7373
page = await browser.new_page()
74-
await page.goto("http://non-existent.test/target.html")
74+
url = "http://staging.example.org/target.html"
75+
requests: List[str] = []
76+
77+
def on_request(request: Request) -> None:
78+
if request.is_navigation_request():
79+
requests.append(request.url)
80+
81+
page.on("request", on_request)
82+
await page.goto(url)
7583
assert await page.title() == "Served by the proxy"
84+
assert page.url == url
85+
assert requests == [url]
7686
await browser.close()
7787

7888

0 commit comments

Comments
 (0)