diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index b7b8f0ad8..f78ba82c0 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -142,7 +142,11 @@ async def launch_persistent_context( raise e async def connect_over_cdp( - self, endpointURL: str, timeout: float = None, slow_mo: float = None + self, + endpointURL: str, + timeout: float = None, + slow_mo: float = None, + headers: Dict[str, str] = None, ) -> Browser: params = locals_to_params(locals()) params["sdkLanguage"] = ( @@ -162,9 +166,13 @@ async def connect_over_cdp( return browser async def connect( - self, ws_endpoint: str, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + timeout: float = None, + slow_mo: float = None, + headers: Dict[str, str] = None, ) -> Browser: - transport = WebSocketTransport(ws_endpoint, timeout) + transport = WebSocketTransport(ws_endpoint, timeout, headers) connection = Connection( self._connection._dispatcher_fiber, diff --git a/playwright/_impl/_transport.py b/playwright/_impl/_transport.py index fb20f3392..b315318b3 100644 --- a/playwright/_impl/_transport.py +++ b/playwright/_impl/_transport.py @@ -137,13 +137,16 @@ def send(self, message: Dict) -> None: class WebSocketTransport(AsyncIOEventEmitter, Transport): - def __init__(self, ws_endpoint: str, timeout: float = None) -> None: + def __init__( + self, ws_endpoint: str, timeout: float = None, headers: Dict[str, str] = None + ) -> None: super().__init__() Transport.__init__(self) self._stopped = False self.ws_endpoint = ws_endpoint self.timeout = timeout + self.headers = headers self._loop: asyncio.AbstractEventLoop def request_stop(self) -> None: @@ -159,10 +162,12 @@ async def wait_until_stopped(self) -> None: async def run(self) -> None: await super().run() - options = {} + options: Dict[str, Any] = {} if self.timeout is not None: options["close_timeout"] = self.timeout / 1000 options["ping_timeout"] = self.timeout / 1000 + if self.headers is not None: + options["extra_headers"] = self.headers self._connection = await websockets.connect(self.ws_endpoint, **options) while not self._stopped: diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 0e573e32d..39b6f10c4 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -560,7 +560,7 @@ async def handle(route, request): \"foo\": \"bar\" # set \"foo\" header \"origin\": None # remove \"origin\" header } - await route.continue(headers=headers) + await route.continue_(headers=headers) } await page.route(\"**/*\", handle) ``` @@ -6384,6 +6384,18 @@ async def route( await browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + await page.route(\"/api/**\", handle_route) + ``` + Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request matches both handlers. @@ -7865,13 +7877,15 @@ def expect_request( ) -> AsyncEventContextManager["Request"]: """Page.expect_request - Waits for the matching request and returns it. + Waits for the matching request and returns it. See [waiting for event](./events.md#waiting-for-event) for more details + about events. ```py async with page.expect_request(\"http://example.com/resource\") as first: await page.click('button') first_request = await first.value + # or with a lambda async with page.expect_request(lambda request: request.url == \"http://example.com\" and request.method == \"get\") as second: await page.click('img') second_request = await second.value @@ -7906,7 +7920,7 @@ def expect_response( ) -> AsyncEventContextManager["Response"]: """Page.expect_response - Returns the matched response. + Returns the matched response. See [waiting for event](./events.md#waiting-for-event) for more details about events. ```py async with page.expect_response(\"https://example.com/resource\") as response_info: @@ -8481,6 +8495,18 @@ async def route( await browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + await context.route(\"/api/**\", handle_route) + ``` + Page routes (set up with `page.route()`) take precedence over browser context routes when request matches both handlers. @@ -9567,7 +9593,12 @@ async def launch_persistent_context( ) async def connect_over_cdp( - self, endpoint_url: str, *, timeout: float = None, slow_mo: float = None + self, + endpoint_url: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect_over_cdp @@ -9588,6 +9619,8 @@ async def connect_over_cdp( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with connect request. Optional. Returns ------- @@ -9598,13 +9631,21 @@ async def connect_over_cdp( await self._async( "browser_type.connect_over_cdp", self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, timeout=timeout, slow_mo=slow_mo + endpointURL=endpoint_url, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) async def connect( - self, ws_endpoint: str, *, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect @@ -9620,6 +9661,8 @@ async def connect( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with web socket connect request. Optional. Returns ------- @@ -9630,7 +9673,10 @@ async def connect( await self._async( "browser_type.connect", self._impl_obj.connect( - ws_endpoint=ws_endpoint, timeout=timeout, slow_mo=slow_mo + ws_endpoint=ws_endpoint, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 48647b948..f4c244b0f 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -560,7 +560,7 @@ def handle(route, request): \"foo\": \"bar\" # set \"foo\" header \"origin\": None # remove \"origin\" header } - route.continue(headers=headers) + route.continue_(headers=headers) } page.route(\"**/*\", handle) ``` @@ -6345,6 +6345,18 @@ def route( browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + page.route(\"/api/**\", handle_route) + ``` + Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request matches both handlers. @@ -7819,13 +7831,15 @@ def expect_request( ) -> EventContextManager["Request"]: """Page.expect_request - Waits for the matching request and returns it. + Waits for the matching request and returns it. See [waiting for event](./events.md#waiting-for-event) for more details + about events. ```py with page.expect_request(\"http://example.com/resource\") as first: page.click('button') first_request = first.value + # or with a lambda with page.expect_request(lambda request: request.url == \"http://example.com\" and request.method == \"get\") as second: page.click('img') second_request = second.value @@ -7860,7 +7874,7 @@ def expect_response( ) -> EventContextManager["Response"]: """Page.expect_response - Returns the matched response. + Returns the matched response. See [waiting for event](./events.md#waiting-for-event) for more details about events. ```py with page.expect_response(\"https://example.com/resource\") as response_info: @@ -8427,6 +8441,18 @@ def route( browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + context.route(\"/api/**\", handle_route) + ``` + Page routes (set up with `page.route()`) take precedence over browser context routes when request matches both handlers. @@ -9513,7 +9539,12 @@ def launch_persistent_context( ) def connect_over_cdp( - self, endpoint_url: str, *, timeout: float = None, slow_mo: float = None + self, + endpoint_url: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect_over_cdp @@ -9534,6 +9565,8 @@ def connect_over_cdp( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with connect request. Optional. Returns ------- @@ -9544,13 +9577,21 @@ def connect_over_cdp( self._sync( "browser_type.connect_over_cdp", self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, timeout=timeout, slow_mo=slow_mo + endpointURL=endpoint_url, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) def connect( - self, ws_endpoint: str, *, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect @@ -9566,6 +9607,8 @@ def connect( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with web socket connect request. Optional. Returns ------- @@ -9576,7 +9619,10 @@ def connect( self._sync( "browser_type.connect", self._impl_obj.connect( - ws_endpoint=ws_endpoint, timeout=timeout, slow_mo=slow_mo + ws_endpoint=ws_endpoint, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) diff --git a/setup.py b/setup.py index 6d3fdf500..8c69f92e3 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.11.0-next-1619111599000" +driver_version = "1.11.0-next-1619452681000" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_download.py b/tests/async/test_download.py index 0bc939505..0708a34d1 100644 --- a/tests/async/test_download.py +++ b/tests/async/test_download.py @@ -194,7 +194,7 @@ async def test_should_error_when_saving_after_deletion(tmpdir, browser, server): await download.delete() with pytest.raises(Error) as exc: await download.save_as(user_path) - assert "File already deleted. Save before deleting." in exc.value.message + assert "Target page, context or browser has been closed" in exc.value.message await page.close()