Skip to content

Commit 3d1b875

Browse files
authored
chore: roll to 1.57.0-beta-1763718928000 (#3007)
1 parent 75ceca0 commit 3d1b875

24 files changed

+639
-1065
lines changed

README.md

Lines changed: 2 additions & 2 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 -->141.0.7390.37<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->143.0.7499.4<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->142.0.1<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->144.0.2<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_accessibility.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

playwright/_impl/_browser_context.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,13 @@ def _on_request_finished(
688688

689689
def _on_console_message(self, event: Dict) -> None:
690690
message = ConsoleMessage(event, self._loop, self._dispatcher_fiber)
691-
self.emit(BrowserContext.Events.Console, message)
691+
worker = message.worker
692+
if worker:
693+
worker.emit(Worker.Events.Console, message)
692694
page = message.page
693695
if page:
694696
page.emit(Page.Events.Console, message)
697+
self.emit(BrowserContext.Events.Console, message)
695698

696699
def _on_dialog(self, dialog: Dialog) -> None:
697700
has_listeners = self.emit(BrowserContext.Events.Dialog, dialog)

playwright/_impl/_console_message.py

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

2222
if TYPE_CHECKING: # pragma: no cover
2323
from playwright._impl._page import Page
24+
from playwright._impl._worker import Worker
2425

2526

2627
class ConsoleMessage:
@@ -31,6 +32,7 @@ def __init__(
3132
self._loop = loop
3233
self._dispatcher_fiber = dispatcher_fiber
3334
self._page: Optional["Page"] = from_nullable_channel(event.get("page"))
35+
self._worker: Optional["Worker"] = from_nullable_channel(event.get("worker"))
3436

3537
def __repr__(self) -> str:
3638
return f"<ConsoleMessage type={self.type} text={self.text}>"
@@ -76,3 +78,7 @@ def location(self) -> SourceLocation:
7678
@property
7779
def page(self) -> Optional["Page"]:
7880
return self._page
81+
82+
@property
83+
def worker(self) -> Optional["Worker"]:
84+
return self._worker

playwright/_impl/_element_handle.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ async def click(
138138
force: bool = None,
139139
noWaitAfter: bool = None,
140140
trial: bool = None,
141+
steps: int = None,
141142
) -> None:
142143
await self._channel.send(
143144
"click", self._frame._timeout, locals_to_params(locals())
@@ -153,6 +154,7 @@ async def dblclick(
153154
force: bool = None,
154155
noWaitAfter: bool = None,
155156
trial: bool = None,
157+
steps: int = None,
156158
) -> None:
157159
await self._channel.send(
158160
"dblclick", self._frame._timeout, locals_to_params(locals())

playwright/_impl/_frame.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,23 @@ async def click(
545545
noWaitAfter: bool = None,
546546
strict: bool = None,
547547
trial: bool = None,
548+
) -> None:
549+
await self._click(**locals_to_params(locals()))
550+
551+
async def _click(
552+
self,
553+
selector: str,
554+
modifiers: Sequence[KeyboardModifier] = None,
555+
position: Position = None,
556+
delay: float = None,
557+
button: MouseButton = None,
558+
clickCount: int = None,
559+
timeout: float = None,
560+
force: bool = None,
561+
noWaitAfter: bool = None,
562+
strict: bool = None,
563+
trial: bool = None,
564+
steps: int = None,
548565
) -> None:
549566
await self._channel.send("click", self._timeout, locals_to_params(locals()))
550567

@@ -734,6 +751,7 @@ async def drag_and_drop(
734751
strict: bool = None,
735752
timeout: float = None,
736753
trial: bool = None,
754+
steps: int = None,
737755
) -> None:
738756
await self._channel.send(
739757
"dragAndDrop", self._timeout, locals_to_params(locals())

playwright/_impl/_glob.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,21 @@ def glob_to_regex_pattern(glob: str) -> str:
2828
tokens.append("\\" + char if char in escaped_chars else char)
2929
i += 1
3030
elif c == "*":
31+
char_before = glob[i - 1] if i > 0 else None
3132
star_count = 1
3233
while i + 1 < len(glob) and glob[i + 1] == "*":
3334
star_count += 1
3435
i += 1
3536
if star_count > 1:
36-
tokens.append("(.*)")
37+
char_after = glob[i + 1] if i + 1 < len(glob) else None
38+
if char_after == "/":
39+
if char_before == "/":
40+
tokens.append("((.+/)|)")
41+
else:
42+
tokens.append("(.*/)")
43+
i += 1
44+
else:
45+
tokens.append("(.*)")
3746
else:
3847
tokens.append("([^/]*)")
3948
else:

playwright/_impl/_helper.py

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
Union,
3636
cast,
3737
)
38-
from urllib.parse import urljoin, urlparse
38+
from urllib.parse import ParseResult, urljoin, urlparse, urlunparse
3939

4040
from playwright._impl._api_structures import NameValue
4141
from playwright._impl._errors import (
@@ -210,8 +210,12 @@ def map_token(original: str, replacement: str) -> str:
210210
# Handle special case of http*://, note that the new schema has to be
211211
# a web schema so that slashes are properly inserted after domain.
212212
if index == 0 and token.endswith(":"):
213-
# Using a simple replacement for the scheme part
214-
processed_parts.append(map_token(token, "http:"))
213+
# Replace any pattern with http:
214+
if "*" in token or "{" in token:
215+
processed_parts.append(map_token(token, "http:"))
216+
else:
217+
# Preserve explicit schema as is as it may affect trailing slashes after domain.
218+
processed_parts.append(token)
215219
continue
216220
question_index = token.find("?")
217221
if question_index == -1:
@@ -222,55 +226,49 @@ def map_token(original: str, replacement: str) -> str:
222226
processed_parts.append(new_prefix + new_suffix)
223227

224228
relative_path = "/".join(processed_parts)
225-
resolved_url, case_insensitive_part = resolve_base_url(base_url, relative_path)
229+
resolved, case_insensitive_part = resolve_base_url(base_url, relative_path)
226230

227-
for replacement, original in token_map.items():
228-
normalize = case_insensitive_part and replacement in case_insensitive_part
229-
resolved_url = resolved_url.replace(
230-
replacement, original.lower() if normalize else original, 1
231+
for token, original in token_map.items():
232+
normalize = case_insensitive_part and token in case_insensitive_part
233+
resolved = resolved.replace(
234+
token, original.lower() if normalize else original, 1
231235
)
232236

233-
return ensure_trailing_slash(resolved_url)
237+
return resolved
234238

235239

236240
def resolve_base_url(
237241
base_url: Optional[str], given_url: str
238242
) -> Tuple[str, Optional[str]]:
239243
try:
240-
resolved = urljoin(base_url if base_url is not None else "", given_url)
241-
parsed = urlparse(resolved)
244+
url = nodelike_urlparse(
245+
urljoin(base_url if base_url is not None else "", given_url)
246+
)
247+
resolved = urlunparse(url)
242248
# Schema and domain are case-insensitive.
243249
hostname_port = (
244-
parsed.hostname or ""
250+
url.hostname or ""
245251
) # can't use parsed.netloc because it includes userinfo (username:password)
246-
if parsed.port:
247-
hostname_port += f":{parsed.port}"
248-
case_insensitive_prefix = f"{parsed.scheme}://{hostname_port}"
252+
if url.port:
253+
hostname_port += f":{url.port}"
254+
case_insensitive_prefix = f"{url.scheme}://{hostname_port}"
249255
return resolved, case_insensitive_prefix
250256
except Exception:
251257
return given_url, None
252258

253259

254-
# In Node.js, new URL('http://localhost') returns 'http://localhost/'.
255-
# To ensure the same url matching behavior, do the same.
256-
def ensure_trailing_slash(url: str) -> str:
257-
split = url.split("://", maxsplit=1)
258-
if len(split) == 2:
259-
# URL parser doesn't like strange/unknown schemes, so we replace it for parsing, then put it back
260-
parsable_url = "http://" + split[1]
261-
else:
262-
# Given current rules, this should never happen _and_ still be a valid matcher. We require the protocol to be part of the match,
263-
# so either the user is using a glob that starts with "*" (and none of this code is running), or the user actually has `something://` in `match`
264-
parsable_url = url
265-
parsed = urlparse(parsable_url, allow_fragments=True)
266-
if len(split) == 2:
267-
# Replace the scheme that we removed earlier
268-
parsed = parsed._replace(scheme=split[0])
269-
if parsed.path == "":
270-
parsed = parsed._replace(path="/")
271-
url = parsed.geturl()
272-
273-
return url
260+
def nodelike_urlparse(url: str) -> ParseResult:
261+
parsed = urlparse(url, allow_fragments=True)
262+
263+
# https://url.spec.whatwg.org/#special-scheme
264+
is_special_url = parsed.scheme in ["http", "https", "ws", "wss", "ftp", "file"]
265+
if is_special_url:
266+
# special urls have a list path, list paths are serialized as follows: https://url.spec.whatwg.org/#url-path-serializer
267+
# urllib diverges, so we patch it here
268+
if parsed.path == "":
269+
parsed = parsed._replace(path="/")
270+
271+
return parsed
274272

275273

276274
class HarLookupResult(TypedDict, total=False):

playwright/_impl/_locator.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import json
1616
import pathlib
17+
import re
1718
from typing import (
1819
TYPE_CHECKING,
1920
Any,
@@ -155,9 +156,10 @@ async def click(
155156
force: bool = None,
156157
noWaitAfter: bool = None,
157158
trial: bool = None,
159+
steps: int = None,
158160
) -> None:
159161
params = locals_to_params(locals())
160-
return await self._frame.click(self._selector, strict=True, **params)
162+
return await self._frame._click(self._selector, strict=True, **params)
161163

162164
async def dblclick(
163165
self,
@@ -169,6 +171,7 @@ async def dblclick(
169171
force: bool = None,
170172
noWaitAfter: bool = None,
171173
trial: bool = None,
174+
steps: int = None,
172175
) -> None:
173176
params = locals_to_params(locals())
174177
return await self._frame.dblclick(self._selector, strict=True, **params)
@@ -343,6 +346,20 @@ def describe(self, description: str) -> "Locator":
343346
f"{self._selector} >> internal:describe={json.dumps(description)}",
344347
)
345348

349+
@property
350+
def description(self) -> Optional[str]:
351+
try:
352+
match = re.search(
353+
r' >> internal:describe=("(?:[^"\\]|\\.)*")$', self._selector
354+
)
355+
if match:
356+
description = json.loads(match.group(1))
357+
if isinstance(description, str):
358+
return description
359+
except (json.JSONDecodeError, ValueError):
360+
pass
361+
return None
362+
346363
def filter(
347364
self,
348365
hasText: Union[str, Pattern[str]] = None,
@@ -414,6 +431,7 @@ async def drag_to(
414431
trial: bool = None,
415432
sourcePosition: Position = None,
416433
targetPosition: Position = None,
434+
steps: int = None,
417435
) -> None:
418436
params = locals_to_params(locals())
419437
del params["target"]

0 commit comments

Comments
 (0)