Skip to content

Commit 6af4fb2

Browse files
authored
chore: roll to 1.54.0 (#2913)
1 parent 3f95752 commit 6af4fb2

31 files changed

+338
-92
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
44

55
| | Linux | macOS | Windows |
66
| :--- | :---: | :---: | :---: |
7-
| Chromium <!-- GEN:chromium-version -->138.0.7204.23<!-- GEN:stop --> ||||
8-
| WebKit <!-- GEN:webkit-version -->18.5<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->139.0<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->139.0.7258.5<!-- GEN:stop --> ||||
8+
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->140.0.2<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_api_structures.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ class Cookie(TypedDict, total=False):
3232
httpOnly: bool
3333
secure: bool
3434
sameSite: Literal["Lax", "None", "Strict"]
35+
partitionKey: Optional[str]
36+
37+
38+
class StorageStateCookie(TypedDict, total=False):
39+
name: str
40+
value: str
41+
domain: str
42+
path: str
43+
expires: float
44+
httpOnly: bool
45+
secure: bool
46+
sameSite: Literal["Lax", "None", "Strict"]
3547

3648

3749
# TODO: We are waiting for PEP705 so SetCookieParam can be readonly and matches Cookie.
@@ -45,6 +57,7 @@ class SetCookieParam(TypedDict, total=False):
4557
httpOnly: Optional[bool]
4658
secure: Optional[bool]
4759
sameSite: Optional[Literal["Lax", "None", "Strict"]]
60+
partitionKey: Optional[str]
4861

4962

5063
class FloatRect(TypedDict):
@@ -97,7 +110,7 @@ class ProxySettings(TypedDict, total=False):
97110

98111

99112
class StorageState(TypedDict, total=False):
100-
cookies: List[Cookie]
113+
cookies: List[StorageStateCookie]
101114
origins: List[OriginState]
102115

103116

playwright/_impl/_assertions.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
AriaRole,
2121
ExpectedTextValue,
2222
FrameExpectOptions,
23+
FrameExpectResult,
2324
)
2425
from playwright._impl._connection import format_call_log
2526
from playwright._impl._errors import Error
@@ -45,6 +46,13 @@ def __init__(
4546
self._is_not = is_not
4647
self._custom_message = message
4748

49+
async def _call_expect(
50+
self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]
51+
) -> FrameExpectResult:
52+
raise NotImplementedError(
53+
"_call_expect must be implemented in a derived class."
54+
)
55+
4856
async def _expect_impl(
4957
self,
5058
expression: str,
@@ -61,7 +69,7 @@ async def _expect_impl(
6169
message = message.replace("expected to", "expected not to")
6270
if "useInnerText" in expect_options and expect_options["useInnerText"] is None:
6371
del expect_options["useInnerText"]
64-
result = await self._actual_locator._expect(expression, expect_options, title)
72+
result = await self._call_expect(expression, expect_options, title)
6573
if result["matches"] == self._is_not:
6674
actual = result.get("received")
6775
if self._custom_message:
@@ -88,6 +96,14 @@ def __init__(
8896
super().__init__(page.locator(":root"), timeout, is_not, message)
8997
self._actual_page = page
9098

99+
async def _call_expect(
100+
self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]
101+
) -> FrameExpectResult:
102+
__tracebackhide__ = True
103+
return await self._actual_page.main_frame._expect(
104+
None, expression, expect_options, title
105+
)
106+
91107
@property
92108
def _not(self) -> "PageAssertions":
93109
return PageAssertions(
@@ -122,7 +138,7 @@ async def to_have_url(
122138
ignoreCase: bool = None,
123139
) -> None:
124140
__tracebackhide__ = True
125-
base_url = self._actual_page.context._options.get("baseURL")
141+
base_url = self._actual_page.context._base_url
126142
if isinstance(urlOrRegExp, str) and base_url:
127143
urlOrRegExp = urljoin(base_url, urlOrRegExp)
128144
expected_text = to_expected_text_values([urlOrRegExp], ignoreCase=ignoreCase)
@@ -155,6 +171,12 @@ def __init__(
155171
super().__init__(locator, timeout, is_not, message)
156172
self._actual_locator = locator
157173

174+
async def _call_expect(
175+
self, expression: str, expect_options: FrameExpectOptions, title: Optional[str]
176+
) -> FrameExpectResult:
177+
__tracebackhide__ = True
178+
return await self._actual_locator._expect(expression, expect_options, title)
179+
158180
@property
159181
def _not(self) -> "LocatorAssertions":
160182
return LocatorAssertions(

playwright/_impl/_browser_context.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def __init__(
119119
self._options: Dict[str, Any] = initializer["options"]
120120
self._background_pages: Set[Page] = set()
121121
self._service_workers: Set[Worker] = set()
122+
self._base_url: Optional[str] = self._options.get("baseURL")
123+
self._videos_dir: Optional[str] = self._options.get("recordVideo")
122124
self._tracing = cast(Tracing, from_channel(initializer["tracing"]))
123125
self._har_recorders: Dict[str, HarRecordingMetadata] = {}
124126
self._request: APIRequestContext = from_channel(initializer["requestContext"])
@@ -424,7 +426,7 @@ async def route(
424426
self._routes.insert(
425427
0,
426428
RouteHandler(
427-
self._options.get("baseURL"),
429+
self._base_url,
428430
url,
429431
handler,
430432
True if self._dispatcher_fiber else False,
@@ -452,17 +454,16 @@ async def _unroute_internal(
452454
behavior: Literal["default", "ignoreErrors", "wait"] = None,
453455
) -> None:
454456
self._routes = remaining
457+
if behavior is not None and behavior != "default":
458+
await asyncio.gather(*map(lambda router: router.stop(behavior), removed)) # type: ignore
455459
await self._update_interception_patterns()
456-
if behavior is None or behavior == "default":
457-
return
458-
await asyncio.gather(*map(lambda router: router.stop(behavior), removed)) # type: ignore
459460

460461
async def route_web_socket(
461462
self, url: URLMatch, handler: WebSocketRouteHandlerCallback
462463
) -> None:
463464
self._web_socket_routes.insert(
464465
0,
465-
WebSocketRouteHandler(self._options.get("baseURL"), url, handler),
466+
WebSocketRouteHandler(self._base_url, url, handler),
466467
)
467468
await self._update_web_socket_interception_patterns()
468469

playwright/_impl/_console_message.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from asyncio import AbstractEventLoop
16-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
16+
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union
1717

1818
from playwright._impl._api_structures import SourceLocation
1919
from playwright._impl._connection import from_channel, from_nullable_channel
@@ -39,7 +39,26 @@ def __str__(self) -> str:
3939
return self.text
4040

4141
@property
42-
def type(self) -> str:
42+
def type(self) -> Union[
43+
Literal["assert"],
44+
Literal["clear"],
45+
Literal["count"],
46+
Literal["debug"],
47+
Literal["dir"],
48+
Literal["dirxml"],
49+
Literal["endGroup"],
50+
Literal["error"],
51+
Literal["info"],
52+
Literal["log"],
53+
Literal["profile"],
54+
Literal["profileEnd"],
55+
Literal["startGroup"],
56+
Literal["startGroupCollapsed"],
57+
Literal["table"],
58+
Literal["timeEnd"],
59+
Literal["trace"],
60+
Literal["warning"],
61+
]:
4362
return self._event["type"]
4463

4564
@property

playwright/_impl/_frame.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@
3030

3131
from pyee import EventEmitter
3232

33-
from playwright._impl._api_structures import AriaRole, FilePayload, Position
33+
from playwright._impl._api_structures import (
34+
AriaRole,
35+
FilePayload,
36+
FrameExpectOptions,
37+
FrameExpectResult,
38+
Position,
39+
)
3440
from playwright._impl._connection import (
3541
ChannelOwner,
3642
from_channel,
@@ -56,6 +62,7 @@
5662
Serializable,
5763
add_source_url_to_script,
5864
parse_result,
65+
parse_value,
5966
serialize_argument,
6067
)
6168
from playwright._impl._locator import (
@@ -170,6 +177,29 @@ def _setup_navigation_waiter(self, wait_name: str, timeout: float = None) -> Wai
170177
waiter.reject_on_timeout(timeout, f"Timeout {timeout}ms exceeded.")
171178
return waiter
172179

180+
async def _expect(
181+
self,
182+
selector: Optional[str],
183+
expression: str,
184+
options: FrameExpectOptions,
185+
title: str = None,
186+
) -> FrameExpectResult:
187+
if "expectedValue" in options:
188+
options["expectedValue"] = serialize_argument(options["expectedValue"])
189+
result = await self._channel.send_return_as_dict(
190+
"expect",
191+
self._timeout,
192+
{
193+
"selector": selector,
194+
"expression": expression,
195+
**options,
196+
},
197+
title=title,
198+
)
199+
if result.get("received"):
200+
result["received"] = parse_value(result["received"])
201+
return result
202+
173203
def expect_navigation(
174204
self,
175205
url: URLMatch = None,
@@ -194,7 +224,7 @@ def predicate(event: Any) -> bool:
194224
return True
195225
waiter.log(f' navigated to "{event["url"]}"')
196226
return url_matches(
197-
cast("Page", self._page)._browser_context._options.get("baseURL"),
227+
cast("Page", self._page)._browser_context._base_url,
198228
event["url"],
199229
url,
200230
)
@@ -227,9 +257,7 @@ async def wait_for_url(
227257
timeout: float = None,
228258
) -> None:
229259
assert self._page
230-
if url_matches(
231-
self._page._browser_context._options.get("baseURL"), self.url, url
232-
):
260+
if url_matches(self._page._browser_context._base_url, self.url, url):
233261
await self._wait_for_load_state_impl(state=waitUntil, timeout=timeout)
234262
return
235263
async with self.expect_navigation(
@@ -558,6 +586,18 @@ async def fill(
558586
noWaitAfter: bool = None,
559587
strict: bool = None,
560588
force: bool = None,
589+
) -> None:
590+
await self._fill(**locals_to_params(locals()))
591+
592+
async def _fill(
593+
self,
594+
selector: str,
595+
value: str,
596+
timeout: float = None,
597+
noWaitAfter: bool = None,
598+
strict: bool = None,
599+
force: bool = None,
600+
title: str = None,
561601
) -> None:
562602
await self._channel.send("fill", self._timeout, locals_to_params(locals()))
563603

@@ -801,7 +841,7 @@ async def uncheck(
801841
await self._channel.send("uncheck", self._timeout, locals_to_params(locals()))
802842

803843
async def wait_for_timeout(self, timeout: float) -> None:
804-
await self._channel.send("waitForTimeout", None, locals_to_params(locals()))
844+
await self._channel.send("waitForTimeout", None, {"waitTimeout": timeout})
805845

806846
async def wait_for_function(
807847
self,

playwright/_impl/_helper.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ def map_token(original: str, replacement: str) -> str:
189189

190190
# Escaped `\\?` behaves the same as `?` in our glob patterns.
191191
match = match.replace(r"\\?", "?")
192+
# Special case about: URLs as they are not relative to base_url
193+
if (
194+
match.startswith("about:")
195+
or match.startswith("data:")
196+
or match.startswith("chrome:")
197+
or match.startswith("edge:")
198+
or match.startswith("file:")
199+
):
200+
# about: and data: URLs are not relative to base_url, so we return them as is.
201+
return match
192202
# Glob symbols may be escaped in the URL and some of them such as ? affect resolution,
193203
# so we replace them with safe components first.
194204
processed_parts = []

playwright/_impl/_locator.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
monotonic_time,
4848
to_impl,
4949
)
50-
from playwright._impl._js_handle import Serializable, parse_value, serialize_argument
50+
from playwright._impl._js_handle import Serializable
5151
from playwright._impl._str_utils import (
5252
escape_for_attribute_selector,
5353
escape_for_text_selector,
@@ -217,7 +217,8 @@ async def clear(
217217
noWaitAfter: bool = None,
218218
force: bool = None,
219219
) -> None:
220-
await self.fill("", timeout=timeout, force=force)
220+
params = locals_to_params(locals())
221+
await self._frame._fill(self._selector, value="", title="Clear", **params)
221222

222223
def locator(
223224
self,
@@ -722,21 +723,7 @@ async def _expect(
722723
options: FrameExpectOptions,
723724
title: str = None,
724725
) -> FrameExpectResult:
725-
if "expectedValue" in options:
726-
options["expectedValue"] = serialize_argument(options["expectedValue"])
727-
result = await self._frame._channel.send_return_as_dict(
728-
"expect",
729-
self._frame._timeout,
730-
{
731-
"selector": self._selector,
732-
"expression": expression,
733-
**options,
734-
},
735-
title=title,
736-
)
737-
if result.get("received"):
738-
result["received"] = parse_value(result["received"])
739-
return result
726+
return await self._frame._expect(self._selector, expression, options, title)
740727

741728
async def highlight(self) -> None:
742729
await self._frame._highlight(self._selector)

playwright/_impl/_network.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,13 @@ async def _after_handle(self) -> None:
733733
if self._connected:
734734
return
735735
# Ensure that websocket is "open" and can send messages without an actual server connection.
736-
await self._channel.send(
737-
"ensureOpened",
738-
None,
739-
)
736+
try:
737+
await self._channel.send(
738+
"ensureOpened",
739+
None,
740+
)
741+
except Exception:
742+
pass
740743

741744

742745
class WebSocketRouteHandler:

0 commit comments

Comments
 (0)