Skip to content

Commit 8c5aaf8

Browse files
authored
chore: roll Playwright to 1.18.0-beta-1642508722000
1 parent b23ec2a commit 8c5aaf8

27 files changed

+1108
-129
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 -->98.0.4714.0<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->99.0.4812.0<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->94.0.1<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->95.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_assertions.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import re
1615
from typing import Any, List, Pattern, Union
1716
from urllib.parse import urljoin
1817

1918
from playwright._impl._api_structures import ExpectedTextValue, FrameExpectOptions
2019
from playwright._impl._locator import Locator
2120
from playwright._impl._page import Page
21+
from playwright._impl._str_utils import escape_regex_flags
2222

2323

2424
class AssertionsBase:
@@ -379,10 +379,13 @@ async def not_to_have_text(
379379
async def to_be_checked(
380380
self,
381381
timeout: float = None,
382+
checked: bool = None,
382383
) -> None:
383384
__tracebackhide__ = True
384385
await self._expect_impl(
385-
"to.be.checked",
386+
"to.be.checked"
387+
if checked is None or checked is True
388+
else "to.be.unchecked",
386389
FrameExpectOptions(timeout=timeout),
387390
None,
388391
"Locator expected to be checked",
@@ -534,27 +537,10 @@ def expected_regex(
534537
) -> ExpectedTextValue:
535538
expected = ExpectedTextValue(
536539
regexSource=pattern.pattern,
540+
regexFlags=escape_regex_flags(pattern),
537541
matchSubstring=match_substring,
538542
normalizeWhiteSpace=normalize_white_space,
539543
)
540-
if pattern.flags != 0:
541-
expected["regexFlags"] = ""
542-
if (pattern.flags & int(re.IGNORECASE)) != 0:
543-
expected["regexFlags"] += "i"
544-
if (pattern.flags & int(re.DOTALL)) != 0:
545-
expected["regexFlags"] += "s"
546-
if (pattern.flags & int(re.MULTILINE)) != 0:
547-
expected["regexFlags"] += "m"
548-
assert (
549-
pattern.flags
550-
& ~(
551-
int(re.MULTILINE)
552-
| int(re.IGNORECASE)
553-
| int(re.DOTALL)
554-
| int(re.UNICODE)
555-
)
556-
== 0
557-
), "Unexpected re.Pattern flag, only MULTILINE, IGNORECASE and DOTALL are supported."
558544
return expected
559545

560546

playwright/_impl/_browser.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
is_safe_close_error,
3737
locals_to_params,
3838
)
39+
from playwright._impl._local_utils import LocalUtils
3940
from playwright._impl._network import serialize_headers
4041
from playwright._impl._page import Page
4142

@@ -59,6 +60,7 @@ def __init__(
5960
self._should_close_connection_on_close = False
6061

6162
self._contexts: List[BrowserContext] = []
63+
_utils: LocalUtils
6264
self._channel.on("close", lambda _: self._on_close())
6365

6466
def __repr__(self) -> str:

playwright/_impl/_browser_context.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
locals_to_params,
4848
to_impl,
4949
)
50+
from playwright._impl._local_utils import LocalUtils
5051
from playwright._impl._network import Request, Response, Route, serialize_headers
5152
from playwright._impl._page import BindingCall, Page, Worker
5253
from playwright._impl._tracing import Tracing
@@ -86,6 +87,7 @@ def __init__(
8687
self._request: APIRequestContext = from_channel(
8788
initializer["APIRequestContext"]
8889
)
90+
_local_utils: LocalUtils
8991
self._channel.on(
9092
"bindingCall",
9193
lambda params: self._on_binding(from_channel(params["binding"])),
@@ -291,7 +293,7 @@ def expect_event(
291293
timeout = self._timeout_settings.timeout()
292294
wait_helper = WaitHelper(self, f"browser_context.expect_event({event})")
293295
wait_helper.reject_on_timeout(
294-
timeout, f'Timeout while waiting for event "{event}"'
296+
timeout, f'Timeout {timeout}ms exceeded while waiting for event "{event}"'
295297
)
296298
if event != BrowserContext.Events.Close:
297299
wait_helper.reject_on_event(

playwright/_impl/_browser_type.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def __init__(
5151
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
5252
) -> None:
5353
super().__init__(parent, type, guid, initializer)
54+
_playwright: "Playwright"
5455

5556
def __repr__(self) -> str:
5657
return f"<BrowserType name={self.name} executable_path={self.executable_path}>"
@@ -85,7 +86,11 @@ async def launch(
8586
) -> Browser:
8687
params = locals_to_params(locals())
8788
normalize_launch_params(params)
88-
return from_channel(await self._channel.send("launch", params))
89+
browser = cast(
90+
Browser, from_channel(await self._channel.send("launch", params))
91+
)
92+
browser._utils = self._playwright._utils
93+
return browser
8994

9095
async def launch_persistent_context(
9196
self,
@@ -142,6 +147,7 @@ async def launch_persistent_context(
142147
await self._channel.send("launchPersistentContext", params)
143148
)
144149
context._options = params
150+
context._local_utils = self._playwright._utils
145151
return context
146152

147153
async def connect_over_cdp(
@@ -154,6 +160,7 @@ async def connect_over_cdp(
154160
params = locals_to_params(locals())
155161
response = await self._channel.send_return_as_dict("connectOverCDP", params)
156162
browser = cast(Browser, from_channel(response["browser"]))
163+
browser._utils = self._playwright._utils
157164

158165
default_context = cast(
159166
Optional[BrowserContext],
@@ -203,6 +210,7 @@ async def connect(
203210
assert pre_launched_browser
204211
browser = cast(Browser, from_channel(pre_launched_browser))
205212
browser._should_close_connection_on_close = True
213+
browser._utils = self._playwright._utils
206214

207215
def handle_transport_close() -> None:
208216
for context in browser.contexts:

playwright/_impl/_fetch.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,19 @@ async def fetch(
247247
# Cannot call allHeaders() here as the request may be paused inside route handler.
248248
headers_obj = headers or (request.headers if request else None)
249249
serialized_headers = serialize_headers(headers_obj) if headers_obj else None
250-
json_data = None
250+
json_data: Any = None
251251
form_data: Optional[List[NameValue]] = None
252252
multipart_data: Optional[List[FormField]] = None
253253
post_data_buffer: Optional[bytes] = None
254254
if data:
255255
if isinstance(data, str):
256-
post_data_buffer = data.encode()
256+
if is_json_content_type(serialized_headers):
257+
json_data = data
258+
else:
259+
post_data_buffer = data.encode()
257260
elif isinstance(data, bytes):
258261
post_data_buffer = data
259-
elif isinstance(data, (dict, list)):
262+
elif isinstance(data, (dict, list, int, bool)):
260263
json_data = data
261264
else:
262265
raise Error(f"Unsupported 'data' type: {type(data)}")
@@ -290,7 +293,7 @@ async def fetch(
290293
def filter_none(input: Dict) -> Dict:
291294
return {k: v for k, v in input.items() if v is not None}
292295

293-
result = await self._channel.send_return_as_dict(
296+
response = await self._channel.send(
294297
"fetch",
295298
filter_none(
296299
{
@@ -308,9 +311,7 @@ def filter_none(input: Dict) -> Dict:
308311
}
309312
),
310313
)
311-
if result.get("error"):
312-
raise Error(result["error"])
313-
return APIResponse(self, result["response"])
314+
return APIResponse(self, response)
314315

315316
async def storage_state(
316317
self, path: Union[pathlib.Path, str] = None
@@ -337,6 +338,9 @@ def __init__(self, context: APIRequestContext, initializer: Dict) -> None:
337338
self._initializer = initializer
338339
self._headers = network.RawHeaders(initializer["headers"])
339340

341+
def __repr__(self) -> str:
342+
return f"<APIResponse url={self.url!r} status={self.status!r} status_text={self.status_text!r}>"
343+
340344
@property
341345
def ok(self) -> bool:
342346
return self.status >= 200 and self.status <= 299
@@ -395,3 +399,12 @@ async def dispose(self) -> None:
395399

396400
def _fetch_uid(self) -> str:
397401
return self._initializer["fetchUid"]
402+
403+
404+
def is_json_content_type(headers: network.HeadersArray = None) -> bool:
405+
if not headers:
406+
return False
407+
for header in headers:
408+
if header["name"] == "Content-Type":
409+
return header["value"].startswith("application/json")
410+
return False

playwright/_impl/_frame.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import asyncio
1616
import sys
1717
from pathlib import Path
18-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union, cast
18+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Pattern, Set, Union, cast
1919

2020
from pyee import EventEmitter
2121

@@ -102,6 +102,9 @@ def _on_frame_navigated(self, event: FrameNavigatedEvent) -> None:
102102
if "error" not in event and hasattr(self, "_page") and self._page:
103103
self._page.emit("framenavigated", self)
104104

105+
async def _query_count(self, selector: str) -> int:
106+
return await self._channel.send("queryCount", {"selector": selector})
107+
105108
@property
106109
def page(self) -> "Page":
107110
return self._page
@@ -495,11 +498,8 @@ async def fill(
495498
) -> None:
496499
await self._channel.send("fill", locals_to_params(locals()))
497500

498-
def locator(
499-
self,
500-
selector: str,
501-
) -> Locator:
502-
return Locator(self, selector)
501+
def locator(self, selector: str, has_text: Union[str, Pattern] = None) -> Locator:
502+
return Locator(self, selector, has_text=has_text)
503503

504504
def frame_locator(self, selector: str) -> FrameLocator:
505505
return FrameLocator(self, selector)

playwright/_impl/_local_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from typing import Dict, List
16+
17+
from playwright._impl._api_structures import NameValue
18+
from playwright._impl._connection import ChannelOwner
19+
20+
21+
class LocalUtils(ChannelOwner):
22+
def __init__(
23+
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
24+
) -> None:
25+
super().__init__(parent, type, guid, initializer)
26+
27+
async def zip(self, zip_file: str, entries: List[NameValue]) -> None:
28+
await self._channel.send("zip", {"zipFile": zip_file, "entries": entries})

playwright/_impl/_locator.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Dict,
2323
List,
2424
Optional,
25+
Pattern,
2526
TypeVar,
2627
Union,
2728
)
@@ -42,6 +43,7 @@
4243
monotonic_time,
4344
)
4445
from playwright._impl._js_handle import Serializable, parse_value, serialize_argument
46+
from playwright._impl._str_utils import escape_regex_flags, escape_with_quotes
4547

4648
if sys.version_info >= (3, 8): # pragma: no cover
4749
from typing import Literal
@@ -56,12 +58,23 @@
5658

5759

5860
class Locator:
59-
def __init__(self, frame: "Frame", selector: str) -> None:
61+
def __init__(
62+
self, frame: "Frame", selector: str, has_text: Union[str, Pattern] = None
63+
) -> None:
6064
self._frame = frame
6165
self._selector = selector
6266
self._loop = frame._loop
6367
self._dispatcher_fiber = frame._connection._dispatcher_fiber
6468

69+
if has_text:
70+
if isinstance(has_text, Pattern):
71+
pattern = escape_with_quotes(has_text.pattern, '"')
72+
flags = escape_regex_flags(has_text)
73+
self._selector += f' >> :scope:text-matches({pattern}, "{flags}")'
74+
else:
75+
escaped = escape_with_quotes(has_text, '"')
76+
self._selector += f" >> :scope:has-text({escaped})"
77+
6578
def __repr__(self) -> str:
6679
return f"<Locator frame={self._frame!r} selector={self._selector!r}>"
6780

@@ -170,8 +183,11 @@ async def fill(
170183
def locator(
171184
self,
172185
selector: str,
186+
has_text: Union[str, Pattern] = None,
173187
) -> "Locator":
174-
return Locator(self._frame, f"{self._selector} >> {selector}")
188+
return Locator(
189+
self._frame, f"{self._selector} >> {selector}", has_text=has_text
190+
)
175191

176192
def frame_locator(self, selector: str) -> "FrameLocator":
177193
return FrameLocator(self._frame, self._selector + " >> " + selector)
@@ -208,7 +224,23 @@ async def focus(self, timeout: float = None) -> None:
208224
async def count(
209225
self,
210226
) -> int:
211-
return int(await self.evaluate_all("ee => ee.length"))
227+
return await self._frame._query_count(self._selector)
228+
229+
async def drag_to(
230+
self,
231+
target: "Locator",
232+
force: bool = None,
233+
noWaitAfter: bool = None,
234+
timeout: float = None,
235+
trial: bool = None,
236+
sourcePosition: Position = None,
237+
targetPosition: Position = None,
238+
) -> None:
239+
params = locals_to_params(locals())
240+
del params["target"]
241+
return await self._frame.drag_and_drop(
242+
self._selector, target._selector, strict=True, **params
243+
)
212244

213245
async def get_attribute(self, name: str, timeout: float = None) -> Optional[str]:
214246
params = locals_to_params(locals())
@@ -506,9 +538,11 @@ def __init__(self, frame: "Frame", frame_selector: str) -> None:
506538
self._dispatcher_fiber = frame._connection._dispatcher_fiber
507539
self._frame_selector = frame_selector
508540

509-
def locator(self, selector: str) -> Locator:
541+
def locator(self, selector: str, has_text: Union[str, Pattern] = None) -> Locator:
510542
return Locator(
511-
self._frame, f"{self._frame_selector} >> control=enter-frame >> {selector}"
543+
self._frame,
544+
f"{self._frame_selector} >> control=enter-frame >> {selector}",
545+
has_text=has_text,
512546
)
513547

514548
def frame_locator(self, selector: str) -> "FrameLocator":

0 commit comments

Comments
 (0)