diff --git a/README.md b/README.md index 39d199a75..eaf9a473a 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 117.0.5938.35 | ✅ | ✅ | ✅ | +| Chromium 117.0.5938.62 | ✅ | ✅ | ✅ | | WebKit 17.0 | ✅ | ✅ | ✅ | -| Firefox 115.0 | ✅ | ✅ | ✅ | +| Firefox 117.0 | ✅ | ✅ | ✅ | ## Documentation diff --git a/playwright/_impl/_browser_context.py b/playwright/_impl/_browser_context.py index 03920bcde..acec3eafb 100644 --- a/playwright/_impl/_browser_context.py +++ b/playwright/_impl/_browser_context.py @@ -69,9 +69,9 @@ ) from playwright._impl._network import Request, Response, Route, serialize_headers from playwright._impl._page import BindingCall, Page, Worker -from playwright._impl._page_error import PageError from playwright._impl._tracing import Tracing from playwright._impl._wait_helper import WaitHelper +from playwright._impl._web_error import WebError if TYPE_CHECKING: # pragma: no cover from playwright._impl._browser import Browser @@ -89,7 +89,7 @@ class BrowserContext(ChannelOwner): Console="console", Dialog="dialog", Page="page", - PageError="pageerror", + WebError="weberror", ServiceWorker="serviceworker", Request="request", Response="response", @@ -152,8 +152,8 @@ def __init__( "dialog", lambda params: self._on_dialog(from_channel(params["dialog"])) ) self._channel.on( - "pageError", - lambda params: self._on_page_error( + "webError", + lambda params: self._on_web_error( parse_error(params["error"]["error"]), from_nullable_channel(params["page"]), ), @@ -566,8 +566,8 @@ def _on_dialog(self, dialog: Dialog) -> None: else: asyncio.create_task(dialog.dismiss()) - async def _on_page_error(self, error: Error, page: Optional[Page]) -> None: - self.emit(BrowserContext.Events.PageError, PageError(self._loop, page, error)) + async def _on_web_error(self, error: Error, page: Optional[Page]) -> None: + self.emit(BrowserContext.Events.WebError, WebError(self._loop, page, error)) if page: page.emit(Page.Events.PageError, error) diff --git a/playwright/_impl/_js_handle.py b/playwright/_impl/_js_handle.py index b23b61ced..8d3aab318 100644 --- a/playwright/_impl/_js_handle.py +++ b/playwright/_impl/_js_handle.py @@ -126,6 +126,8 @@ def serialize_value( return dict(v="-0") if math.isnan(value): return dict(v="NaN") + if isinstance(value, set): + return {"se": serialize_value(list(value), handles, visitor_info)} if isinstance(value, datetime): return dict(d=value.isoformat() + "Z") if isinstance(value, bool): @@ -195,6 +197,12 @@ def parse_value(value: Any, refs: Optional[Dict[int, Any]] = None) -> Any: if "bi" in value: return int(value["bi"]) + if "m" in value: + return dict(parse_value(value["m"], refs)) + + if "se" in value: + return set(parse_value(value["se"], refs)) + if "a" in value: a: List = [] refs[value["id"]] = a diff --git a/playwright/_impl/_page_error.py b/playwright/_impl/_web_error.py similarity index 98% rename from playwright/_impl/_page_error.py rename to playwright/_impl/_web_error.py index d57bbf9e2..eb1b51948 100644 --- a/playwright/_impl/_page_error.py +++ b/playwright/_impl/_web_error.py @@ -19,7 +19,7 @@ from playwright._impl._page import Page -class PageError: +class WebError: def __init__( self, loop: AbstractEventLoop, page: Optional[Page], error: Error ) -> None: diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 65c2d0f2c..baebb4265 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -79,11 +79,11 @@ from playwright._impl._network import WebSocket as WebSocketImpl from playwright._impl._page import Page as PageImpl from playwright._impl._page import Worker as WorkerImpl -from playwright._impl._page_error import PageError as PageErrorImpl from playwright._impl._playwright import Playwright as PlaywrightImpl from playwright._impl._selectors import Selectors as SelectorsImpl from playwright._impl._tracing import Tracing as TracingImpl from playwright._impl._video import Video as VideoImpl +from playwright._impl._web_error import WebError as WebErrorImpl class Request(AsyncBase): @@ -1302,6 +1302,9 @@ async def insert_text(self, text: str) -> None: async def type(self, text: str, *, delay: typing.Optional[float] = None) -> None: """Keyboard.type + **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if + there is special keyboard handling on the page - in this case use `locator.press_sequentially()`. + Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`. @@ -1337,6 +1340,8 @@ async def type(self, text: str, *, delay: typing.Optional[float] = None) -> None async def press(self, key: str, *, delay: typing.Optional[float] = None) -> None: """Keyboard.press + **NOTE** In most cases, you should use `locator.press()` instead. + `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character to generate the text for. A superset of the `key` values can be found @@ -2335,7 +2340,7 @@ async def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `keyboard.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -4633,7 +4638,7 @@ async def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `frame.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -10278,7 +10283,7 @@ async def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `page.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -12484,10 +12489,10 @@ async def set_checked( mapping.register(PageImpl, Page) -class PageError(AsyncBase): +class WebError(AsyncBase): @property def page(self) -> typing.Optional["Page"]: - """PageError.page + """WebError.page The page that produced this unhandled exception, if any. @@ -12499,7 +12504,7 @@ def page(self) -> typing.Optional["Page"]: @property def error(self) -> "Error": - """PageError.error + """WebError.error Unhandled error that was thrown. @@ -12510,7 +12515,7 @@ def error(self) -> "Error": return mapping.from_impl(self._impl_obj.error) -mapping.register(PageErrorImpl, PageError) +mapping.register(WebErrorImpl, WebError) class BrowserContext(AsyncContextManager): @@ -12639,12 +12644,12 @@ def on( @typing.overload def on( self, - event: Literal["pageerror"], - f: typing.Callable[["PageError"], "typing.Union[typing.Awaitable[None], None]"], + event: Literal["weberror"], + f: typing.Callable[["WebError"], "typing.Union[typing.Awaitable[None], None]"], ) -> None: """ - Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError` - events from a particular page, use `page.on('page_error')`.""" + Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular + page, use `page.on('page_error')` instead.""" @typing.overload def on( @@ -12838,12 +12843,12 @@ def once( @typing.overload def once( self, - event: Literal["pageerror"], - f: typing.Callable[["PageError"], "typing.Union[typing.Awaitable[None], None]"], + event: Literal["weberror"], + f: typing.Callable[["WebError"], "typing.Union[typing.Awaitable[None], None]"], ) -> None: """ - Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError` - events from a particular page, use `page.on('page_error')`.""" + Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular + page, use `page.on('page_error')` instead.""" @typing.overload def once( diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 775bc81b3..f4e575041 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -73,7 +73,6 @@ from playwright._impl._network import WebSocket as WebSocketImpl from playwright._impl._page import Page as PageImpl from playwright._impl._page import Worker as WorkerImpl -from playwright._impl._page_error import PageError as PageErrorImpl from playwright._impl._playwright import Playwright as PlaywrightImpl from playwright._impl._selectors import Selectors as SelectorsImpl from playwright._impl._sync_base import ( @@ -84,6 +83,7 @@ ) from playwright._impl._tracing import Tracing as TracingImpl from playwright._impl._video import Video as VideoImpl +from playwright._impl._web_error import WebError as WebErrorImpl class Request(SyncBase): @@ -1300,6 +1300,9 @@ def insert_text(self, text: str) -> None: def type(self, text: str, *, delay: typing.Optional[float] = None) -> None: """Keyboard.type + **NOTE** In most cases, you should use `locator.fill()` instead. You only need to press keys one by one if + there is special keyboard handling on the page - in this case use `locator.press_sequentially()`. + Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. To press a special key, like `Control` or `ArrowDown`, use `keyboard.press()`. @@ -1335,6 +1338,8 @@ def type(self, text: str, *, delay: typing.Optional[float] = None) -> None: def press(self, key: str, *, delay: typing.Optional[float] = None) -> None: """Keyboard.press + **NOTE** In most cases, you should use `locator.press()` instead. + `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) value or a single character to generate the text for. A superset of the `key` values can be found @@ -2353,7 +2358,7 @@ def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `keyboard.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -4721,7 +4726,7 @@ def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `frame.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -10354,7 +10359,7 @@ def fill( [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled instead. - To send fine-grained keyboard events, use `page.type()`. + To send fine-grained keyboard events, use `locator.press_sequentially()`. Parameters ---------- @@ -12594,10 +12599,10 @@ def set_checked( mapping.register(PageImpl, Page) -class PageError(SyncBase): +class WebError(SyncBase): @property def page(self) -> typing.Optional["Page"]: - """PageError.page + """WebError.page The page that produced this unhandled exception, if any. @@ -12609,7 +12614,7 @@ def page(self) -> typing.Optional["Page"]: @property def error(self) -> "Error": - """PageError.error + """WebError.error Unhandled error that was thrown. @@ -12620,7 +12625,7 @@ def error(self) -> "Error": return mapping.from_impl(self._impl_obj.error) -mapping.register(PageErrorImpl, PageError) +mapping.register(WebErrorImpl, WebError) class BrowserContext(SyncContextManager): @@ -12732,11 +12737,11 @@ def on(self, event: Literal["page"], f: typing.Callable[["Page"], "None"]) -> No @typing.overload def on( - self, event: Literal["pageerror"], f: typing.Callable[["PageError"], "None"] + self, event: Literal["weberror"], f: typing.Callable[["WebError"], "None"] ) -> None: """ - Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError` - events from a particular page, use `page.on('page_error')`.""" + Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular + page, use `page.on('page_error')` instead.""" @typing.overload def on( @@ -12901,11 +12906,11 @@ def once( @typing.overload def once( - self, event: Literal["pageerror"], f: typing.Callable[["PageError"], "None"] + self, event: Literal["weberror"], f: typing.Callable[["WebError"], "None"] ) -> None: """ - Emitted when unhandled exceptions occur on any pages created through this context. To only listen for `pageError` - events from a particular page, use `page.on('page_error')`.""" + Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular + page, use `page.on('page_error')` instead.""" @typing.overload def once( diff --git a/scripts/generate_api.py b/scripts/generate_api.py index bfd850593..da5cc8ed2 100644 --- a/scripts/generate_api.py +++ b/scripts/generate_api.py @@ -51,11 +51,11 @@ from playwright._impl._locator import FrameLocator, Locator from playwright._impl._network import Request, Response, Route, WebSocket from playwright._impl._page import Page, Worker -from playwright._impl._page_error import PageError from playwright._impl._playwright import Playwright from playwright._impl._selectors import Selectors from playwright._impl._tracing import Tracing from playwright._impl._video import Video +from playwright._impl._web_error import WebError def process_type(value: Any, param: bool = False) -> str: @@ -240,7 +240,7 @@ def return_value(value: Any) -> List[str]: from playwright._impl._js_handle import JSHandle as JSHandleImpl from playwright._impl._network import Request as RequestImpl, Response as ResponseImpl, Route as RouteImpl, WebSocket as WebSocketImpl from playwright._impl._page import Page as PageImpl, Worker as WorkerImpl -from playwright._impl._page_error import PageError as PageErrorImpl +from playwright._impl._web_error import WebError as WebErrorImpl from playwright._impl._playwright import Playwright as PlaywrightImpl from playwright._impl._selectors import Selectors as SelectorsImpl from playwright._impl._video import Video as VideoImpl @@ -273,7 +273,7 @@ def return_value(value: Any) -> List[str]: Download, Video, Page, - PageError, + WebError, BrowserContext, CDPSession, Browser, diff --git a/setup.py b/setup.py index 12056ccef..f8d3c4046 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.38.0-alpha-sep-4-2023" +driver_version = "1.38.0" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_browsercontext_events.py b/tests/async/test_browsercontext_events.py index e65b0750c..dd8642786 100644 --- a/tests/async/test_browsercontext_events.py +++ b/tests/async/test_browsercontext_events.py @@ -191,7 +191,7 @@ async def test_console_event_should_work_with_context_manager(page: Page) -> Non async def test_page_error_event_should_work(page: Page) -> None: - async with page.context.expect_event("pageerror") as page_error_info: + async with page.context.expect_event("weberror") as page_error_info: await page.set_content('') page_error = await page_error_info.value assert page_error.page == page diff --git a/tests/async/test_evaluate.py b/tests/async/test_evaluate.py index 95c528d33..0dd9914dc 100644 --- a/tests/async/test_evaluate.py +++ b/tests/async/test_evaluate.py @@ -69,6 +69,17 @@ async def test_evaluate_transfer_bigint(page: Page) -> None: assert await page.evaluate("a => a", 17) == 17 +async def test_should_transfer_maps(page): + map_obj = {"foo": "bar"} + result = await page.evaluate("a => new Map(Object.entries(a))", map_obj) + assert result == map_obj + + +async def test_should_transfer_sets(page): + assert await page.evaluate("() => new Set([1, 42n])") == set([1, 42]) + assert await page.evaluate("a => a", set([1, "17"])) == set([1, "17"]) + + async def test_evaluate_return_undefined_for_objects_with_symbols(page): assert await page.evaluate('[Symbol("foo4")]') == [None] assert (