Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(roll): roll Playwright to 1.45.0-alpha-2024-06-14 #2464

Merged
merged 8 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->125.0.6422.26<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Chromium <!-- GEN:chromium-version -->127.0.6533.5<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->125.0.1<!-- GEN:stop --> | ✅ | ✅ | ✅ |
| Firefox <!-- GEN:firefox-version -->127.0<!-- GEN:stop --> | ✅ | ✅ | ✅ |

## Documentation

Expand Down
1 change: 1 addition & 0 deletions playwright/_impl/_api_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class HttpCredentials(TypedDict, total=False):
username: str
password: str
origin: Optional[str]
send: Optional[Literal["always", "unauthorized"]]


class LocalStorageEntry(TypedDict):
Expand Down
10 changes: 10 additions & 0 deletions playwright/_impl/_browser_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
)
from playwright._impl._artifact import Artifact
from playwright._impl._cdp_session import CDPSession
from playwright._impl._clock import Clock
from playwright._impl._connection import (
ChannelOwner,
from_channel,
Expand Down Expand Up @@ -114,6 +115,7 @@ def __init__(
self._tracing = cast(Tracing, from_channel(initializer["tracing"]))
self._har_recorders: Dict[str, HarRecordingMetadata] = {}
self._request: APIRequestContext = from_channel(initializer["requestContext"])
self._clock = Clock(self)
self._channel.on(
"bindingCall",
lambda params: self._on_binding(from_channel(params["binding"])),
Expand Down Expand Up @@ -519,6 +521,10 @@ async def close(self, reason: str = None) -> None:
self._close_reason = reason
self._close_was_called = True

await self._channel._connection.wrap_api_call(
lambda: self.request.dispose(reason=reason), True
)

async def _inner_close() -> None:
for har_id, params in self._har_recorders.items():
har = cast(
Expand Down Expand Up @@ -679,3 +685,7 @@ def tracing(self) -> Tracing:
@property
def request(self) -> "APIRequestContext":
return self._request

@property
def clock(self) -> Clock:
return self._clock
84 changes: 84 additions & 0 deletions playwright/_impl/_clock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import datetime
from typing import TYPE_CHECKING, Dict, Union

if TYPE_CHECKING:
from playwright._impl._browser_context import BrowserContext


class Clock:
def __init__(self, browser_context: "BrowserContext") -> None:
self._browser_context = browser_context
self._loop = browser_context._loop
self._dispatcher_fiber = browser_context._dispatcher_fiber

async def install(self, time: Union[int, str, datetime.datetime] = None) -> None:
await self._browser_context._channel.send(
"clockInstall", parse_time(time) if time is not None else {}
)

async def fast_forward(
self,
ticks: Union[int, str],
) -> None:
await self._browser_context._channel.send(
"clockFastForward", parse_ticks(ticks)
)

async def pause_at(
self,
time: Union[int, str, datetime.datetime],
) -> None:
await self._browser_context._channel.send("clockPauseAt", parse_time(time))

async def resume(
self,
) -> None:
await self._browser_context._channel.send("clockResume")

async def run_for(
self,
ticks: Union[int, str],
) -> None:
await self._browser_context._channel.send("clockRunFor", parse_ticks(ticks))

async def set_fixed_time(
self,
time: Union[int, str, datetime.datetime],
) -> None:
await self._browser_context._channel.send("clockSetFixedTime", parse_time(time))

async def set_system_time(
self,
time: Union[int, str, datetime.datetime],
) -> None:
await self._browser_context._channel.send(
"clockSetSystemTime", parse_time(time)
)


def parse_time(time: Union[int, str, datetime.datetime]) -> Dict[str, Union[int, str]]:
if isinstance(time, int):
return {"timeNumber": time}
if isinstance(time, str):
return {"timeString": time}
return {"timeNumber": int(time.timestamp())}


def parse_ticks(ticks: Union[int, str]) -> Dict[str, Union[int, str]]:
if isinstance(ticks, int):
return {"ticksNumber": ticks}
return {"ticksString": ticks}
14 changes: 12 additions & 2 deletions playwright/_impl/_fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from playwright._impl._helper import (
Error,
NameValue,
TargetClosedError,
async_readfile,
async_writefile,
is_file_payload,
Expand Down Expand Up @@ -93,9 +94,16 @@ def __init__(
) -> None:
super().__init__(parent, type, guid, initializer)
self._tracing: Tracing = from_channel(initializer["tracing"])
self._close_reason: Optional[str] = None

async def dispose(self) -> None:
await self._channel.send("dispose")
async def dispose(self, reason: str = None) -> None:
self._close_reason = reason
try:
await self._channel.send("dispose", {"reason": reason})
except Error as e:
if is_target_closed_error(e):
return
raise e
self._tracing._reset_stack_counter()

async def delete(
Expand Down Expand Up @@ -313,6 +321,8 @@ async def _inner_fetch(
ignoreHTTPSErrors: bool = None,
maxRedirects: int = None,
) -> "APIResponse":
if self._close_reason:
raise TargetClosedError(self._close_reason)
assert (
(1 if data else 0) + (1 if form else 0) + (1 if multipart else 0)
) <= 1, "Only one of 'data', 'form' or 'multipart' can be specified"
Expand Down
16 changes: 15 additions & 1 deletion playwright/_impl/_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@
from urllib.parse import urljoin

from playwright._impl._api_structures import NameValue
from playwright._impl._errors import Error, TargetClosedError, TimeoutError
from playwright._impl._errors import (
Error,
TargetClosedError,
TimeoutError,
is_target_closed_error,
rewrite_error,
)
from playwright._impl._glob import glob_to_regex
from playwright._impl._greenlets import RouteGreenlet
from playwright._impl._str_utils import escape_regex_flags
Expand Down Expand Up @@ -287,6 +293,14 @@ async def handle(self, route: "Route") -> bool:
# If the handler was stopped (without waiting for completion), we ignore all exceptions.
if self._ignore_exception:
return False
if is_target_closed_error(e):
# We are failing in the handler because the target has closed.
# Give user a hint!
optional_async_prefix = "await " if not self._is_sync else ""
raise rewrite_error(
e,
f"\"{str(e)}\" while running route callback.\nConsider awaiting `{optional_async_prefix}page.unroute_all(behavior='ignoreErrors')`\nbefore the end of the test to ignore remaining routes in flight.",
)
raise e
finally:
handler_invocation.complete.set_result(None)
Expand Down
20 changes: 11 additions & 9 deletions playwright/_impl/_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ def __init__(
self._fallback_overrides: SerializedFallbackOverrides = (
SerializedFallbackOverrides()
)
base64_post_data = initializer.get("postData")
if base64_post_data is not None:
self._fallback_overrides.post_data_buffer = base64.b64decode(
base64_post_data
)

def __repr__(self) -> str:
return f"<Request url={self.url!r} method={self.method!r}>"
Expand Down Expand Up @@ -159,9 +154,12 @@ async def sizes(self) -> RequestSizes:
@property
def post_data(self) -> Optional[str]:
data = self._fallback_overrides.post_data_buffer
if not data:
return None
return data.decode()
if data:
return data.decode()
base64_post_data = self._initializer.get("postData")
if base64_post_data is not None:
return base64.b64decode(base64_post_data).decode()
return None

@property
def post_data_json(self) -> Optional[Any]:
Expand All @@ -178,7 +176,11 @@ def post_data_json(self) -> Optional[Any]:

@property
def post_data_buffer(self) -> Optional[bytes]:
return self._fallback_overrides.post_data_buffer
if self._fallback_overrides.post_data_buffer:
return self._fallback_overrides.post_data_buffer
if self._initializer.get("postData"):
return base64.b64decode(self._initializer["postData"])
return None

async def response(self) -> Optional["Response"]:
return from_nullable_channel(await self._channel.send("response"))
Expand Down
5 changes: 5 additions & 0 deletions playwright/_impl/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
ViewportSize,
)
from playwright._impl._artifact import Artifact
from playwright._impl._clock import Clock
from playwright._impl._connection import (
ChannelOwner,
from_channel,
Expand Down Expand Up @@ -336,6 +337,10 @@ def _on_video(self, params: Any) -> None:
def context(self) -> "BrowserContext":
return self._browser_context

@property
def clock(self) -> Clock:
return self._browser_context.clock

async def opener(self) -> Optional["Page"]:
if self._opener and self._opener.is_closed():
return None
Expand Down
Loading
Loading