Skip to content

Commit 92a3316

Browse files
authored
chore: roll to ToT (#1684)
1 parent eaf3f15 commit 92a3316

20 files changed

+1245
-550
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 -->108.0.5359.48<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->109.0.5414.46<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->106.0<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->107.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_element_handle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ def convert_select_option_values(
392392
if value:
393393
if not isinstance(value, list):
394394
value = [value]
395-
options = (options or []) + list(map(lambda e: dict(value=e), value))
395+
options = (options or []) + list(map(lambda e: dict(valueOrLabel=e), value))
396396
if index:
397397
if not isinstance(index, list):
398398
index = [index]

playwright/_impl/_fetch.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ async def fetch(
273273
ignoreHTTPSErrors: bool = None,
274274
maxRedirects: int = None,
275275
) -> "APIResponse":
276+
url = urlOrRequest if isinstance(urlOrRequest, str) else None
276277
request = (
277278
cast(network.Request, to_impl(urlOrRequest))
278279
if isinstance(to_impl(urlOrRequest), network.Request)
@@ -281,13 +282,43 @@ async def fetch(
281282
assert request or isinstance(
282283
urlOrRequest, str
283284
), "First argument must be either URL string or Request"
285+
return await self._inner_fetch(
286+
request,
287+
url,
288+
method,
289+
headers,
290+
data,
291+
params,
292+
form,
293+
multipart,
294+
timeout,
295+
failOnStatusCode,
296+
ignoreHTTPSErrors,
297+
maxRedirects,
298+
)
299+
300+
async def _inner_fetch(
301+
self,
302+
request: Optional[network.Request],
303+
url: Optional[str],
304+
method: str = None,
305+
headers: Headers = None,
306+
data: DataType = None,
307+
params: ParamsType = None,
308+
form: FormType = None,
309+
multipart: Dict[str, Union[bytes, bool, float, str, FilePayload]] = None,
310+
timeout: float = None,
311+
failOnStatusCode: bool = None,
312+
ignoreHTTPSErrors: bool = None,
313+
maxRedirects: int = None,
314+
) -> "APIResponse":
284315
assert (
285316
(1 if data else 0) + (1 if form else 0) + (1 if multipart else 0)
286317
) <= 1, "Only one of 'data', 'form' or 'multipart' can be specified"
287318
assert (
288319
maxRedirects is None or maxRedirects >= 0
289320
), "'max_redirects' must be greater than or equal to '0'"
290-
url = request.url if request else urlOrRequest
321+
url = url or (request.url if request else url)
291322
method = method or (request.method if request else "GET")
292323
# Cannot call allHeaders() here as the request may be paused inside route handler.
293324
headers_obj = headers or (request.headers if request else None)

playwright/_impl/_frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def get_by_role(
578578
)
579579
)
580580

581-
def get_by_test_id(self, testId: str) -> "Locator":
581+
def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator":
582582
return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))
583583

584584
def get_by_text(

playwright/_impl/_helper.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,6 @@ class ErrorPayload(TypedDict, total=False):
7979
value: Optional[Any]
8080

8181

82-
class FallbackOverrideParameters(TypedDict, total=False):
83-
url: Optional[str]
84-
method: Optional[str]
85-
headers: Optional[Dict[str, str]]
86-
postData: Optional[Union[str, bytes]]
87-
88-
8982
class HarRecordingMetadata(TypedDict, total=False):
9083
path: str
9184
content: Optional[HarContentPolicy]

playwright/_impl/_locator.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ def get_by_role(
261261
)
262262
)
263263

264-
def get_by_test_id(self, testId: str) -> "Locator":
264+
def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator":
265265
return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))
266266

267267
def get_by_text(
@@ -328,6 +328,14 @@ async def blur(self, timeout: float = None) -> None:
328328
},
329329
)
330330

331+
async def all(
332+
self,
333+
) -> List["Locator"]:
334+
result = []
335+
for index in range(await self.count()):
336+
result.append(self.nth(index))
337+
return result
338+
331339
async def count(
332340
self,
333341
) -> int:
@@ -709,7 +717,7 @@ def get_by_role(
709717
)
710718
)
711719

712-
def get_by_test_id(self, testId: str) -> "Locator":
720+
def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator":
713721
return self.locator(get_by_test_id_selector(test_id_attribute_name(), testId))
714722

715723
def get_by_text(
@@ -755,7 +763,11 @@ def set_test_id_attribute_name(attribute_name: str) -> None:
755763
_test_id_attribute_name = attribute_name
756764

757765

758-
def get_by_test_id_selector(test_id_attribute_name: str, test_id: str) -> str:
766+
def get_by_test_id_selector(
767+
test_id_attribute_name: str, test_id: Union[str, Pattern[str]]
768+
) -> str:
769+
if isinstance(test_id, Pattern):
770+
return f"internal:testid=[{test_id_attribute_name}=/{test_id.pattern}/{escape_regex_flags(test_id)}]"
759771
return f"internal:testid=[{test_id_attribute_name}={escape_for_attribute_selector(test_id, True)}]"
760772

761773

playwright/_impl/_network.py

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import inspect
1818
import json
1919
import mimetypes
20+
import sys
2021
from collections import defaultdict
2122
from pathlib import Path
2223
from types import SimpleNamespace
@@ -31,6 +32,11 @@
3132
Union,
3233
cast,
3334
)
35+
36+
if sys.version_info >= (3, 8): # pragma: no cover
37+
from typing import TypedDict
38+
else: # pragma: no cover
39+
from typing_extensions import TypedDict
3440
from urllib import parse
3541

3642
from playwright._impl._api_structures import (
@@ -48,7 +54,7 @@
4854
from_nullable_channel,
4955
)
5056
from playwright._impl._event_context_manager import EventContextManagerImpl
51-
from playwright._impl._helper import FallbackOverrideParameters, locals_to_params
57+
from playwright._impl._helper import locals_to_params
5258
from playwright._impl._wait_helper import WaitHelper
5359

5460
if TYPE_CHECKING: # pragma: no cover
@@ -57,6 +63,21 @@
5763
from playwright._impl._page import Page
5864

5965

66+
class FallbackOverrideParameters(TypedDict, total=False):
67+
url: Optional[str]
68+
method: Optional[str]
69+
headers: Optional[Dict[str, str]]
70+
postData: Optional[Union[str, bytes]]
71+
72+
73+
class SerializedFallbackOverrides:
74+
def __init__(self) -> None:
75+
self.url: Optional[str] = None
76+
self.method: Optional[str] = None
77+
self.headers: Optional[Dict[str, str]] = None
78+
self.post_data_buffer: Optional[bytes] = None
79+
80+
6081
def serialize_headers(headers: Dict[str, str]) -> HeadersArray:
6182
return [
6283
{"name": name, "value": value}
@@ -90,31 +111,47 @@ def __init__(
90111
}
91112
self._provisional_headers = RawHeaders(self._initializer["headers"])
92113
self._all_headers_future: Optional[asyncio.Future[RawHeaders]] = None
93-
self._fallback_overrides: FallbackOverrideParameters = (
94-
FallbackOverrideParameters()
114+
self._fallback_overrides: SerializedFallbackOverrides = (
115+
SerializedFallbackOverrides()
95116
)
117+
base64_post_data = initializer.get("postData")
118+
if base64_post_data is not None:
119+
self._fallback_overrides.post_data_buffer = base64.b64decode(
120+
base64_post_data
121+
)
96122

97123
def __repr__(self) -> str:
98124
return f"<Request url={self.url!r} method={self.method!r}>"
99125

100126
def _apply_fallback_overrides(self, overrides: FallbackOverrideParameters) -> None:
101-
self._fallback_overrides = cast(
102-
FallbackOverrideParameters, {**self._fallback_overrides, **overrides}
127+
self._fallback_overrides.url = overrides.get(
128+
"url", self._fallback_overrides.url
103129
)
130+
self._fallback_overrides.method = overrides.get(
131+
"method", self._fallback_overrides.method
132+
)
133+
self._fallback_overrides.headers = overrides.get(
134+
"headers", self._fallback_overrides.headers
135+
)
136+
post_data = overrides.get("postData")
137+
if isinstance(post_data, str):
138+
self._fallback_overrides.post_data_buffer = post_data.encode()
139+
elif isinstance(post_data, bytes):
140+
self._fallback_overrides.post_data_buffer = post_data
141+
elif post_data is not None:
142+
self._fallback_overrides.post_data_buffer = json.dumps(post_data).encode()
104143

105144
@property
106145
def url(self) -> str:
107-
return cast(str, self._fallback_overrides.get("url", self._initializer["url"]))
146+
return cast(str, self._fallback_overrides.url or self._initializer["url"])
108147

109148
@property
110149
def resource_type(self) -> str:
111150
return self._initializer["resourceType"]
112151

113152
@property
114153
def method(self) -> str:
115-
return cast(
116-
str, self._fallback_overrides.get("method", self._initializer["method"])
117-
)
154+
return cast(str, self._fallback_overrides.method or self._initializer["method"])
118155

119156
async def sizes(self) -> RequestSizes:
120157
response = await self.response()
@@ -124,7 +161,7 @@ async def sizes(self) -> RequestSizes:
124161

125162
@property
126163
def post_data(self) -> Optional[str]:
127-
data = self._fallback_overrides.get("postData", self.post_data_buffer)
164+
data = self._fallback_overrides.post_data_buffer
128165
if not data:
129166
return None
130167
return data.decode() if isinstance(data, bytes) else data
@@ -144,17 +181,7 @@ def post_data_json(self) -> Optional[Any]:
144181

145182
@property
146183
def post_data_buffer(self) -> Optional[bytes]:
147-
override = self._fallback_overrides.get("post_data")
148-
if override:
149-
return (
150-
override.encode()
151-
if isinstance(override, str)
152-
else cast(bytes, override)
153-
)
154-
b64_content = self._initializer.get("postData")
155-
if b64_content is None:
156-
return None
157-
return base64.b64decode(b64_content)
184+
return self._fallback_overrides.post_data_buffer
158185

159186
async def response(self) -> Optional["Response"]:
160187
return from_nullable_channel(await self._channel.send("response"))
@@ -189,7 +216,7 @@ def _set_response_end_timing(self, response_end_timing: float) -> None:
189216

190217
@property
191218
def headers(self) -> Headers:
192-
override = self._fallback_overrides.get("headers")
219+
override = self._fallback_overrides.headers
193220
if override:
194221
return RawHeaders._from_headers_dict_lossy(override).headers()
195222
return self._provisional_headers.headers()
@@ -204,7 +231,7 @@ async def header_value(self, name: str) -> Optional[str]:
204231
return (await self._actual_headers()).get(name)
205232

206233
async def _actual_headers(self) -> "RawHeaders":
207-
override = self._fallback_overrides.get("headers")
234+
override = self._fallback_overrides.headers
208235
if override:
209236
return RawHeaders(serialize_headers(override))
210237
if not self._all_headers_future:
@@ -254,6 +281,7 @@ async def fulfill(
254281
status: int = None,
255282
headers: Dict[str, str] = None,
256283
body: Union[str, bytes] = None,
284+
json: Any = None,
257285
path: Union[str, Path] = None,
258286
contentType: str = None,
259287
response: "APIResponse" = None,
@@ -270,6 +298,8 @@ async def fulfill(
270298
)
271299
from playwright._impl._fetch import APIResponse
272300

301+
if json is not None:
302+
body = json.dumps(json)
273303
if body is None and path is None and isinstance(response, APIResponse):
274304
if response._request._connection is self._connection:
275305
params["fetchResponseUid"] = response._fetch_uid
@@ -295,6 +325,8 @@ async def fulfill(
295325
headers = {k.lower(): str(v) for k, v in params.get("headers", {}).items()}
296326
if params.get("contentType"):
297327
headers["content-type"] = params["contentType"]
328+
elif json:
329+
headers["content-type"] = "application/json"
298330
elif path:
299331
headers["content-type"] = (
300332
mimetypes.guess_type(str(Path(path)))[0] or "application/octet-stream"
@@ -305,12 +337,24 @@ async def fulfill(
305337
await self._race_with_page_close(self._channel.send("fulfill", params))
306338
self._report_handled(True)
307339

340+
async def fetch(
341+
self,
342+
url: str = None,
343+
method: str = None,
344+
headers: Dict[str, str] = None,
345+
postData: Union[Any, str, bytes] = None,
346+
) -> "APIResponse":
347+
page = self.request.frame._page
348+
return await page.context.request._inner_fetch(
349+
self.request, url, method, headers, postData
350+
)
351+
308352
async def fallback(
309353
self,
310354
url: str = None,
311355
method: str = None,
312356
headers: Dict[str, str] = None,
313-
postData: Union[str, bytes] = None,
357+
postData: Union[Any, str, bytes] = None,
314358
) -> None:
315359
overrides = cast(FallbackOverrideParameters, locals_to_params(locals()))
316360
self._check_not_handled()
@@ -322,7 +366,7 @@ async def continue_(
322366
url: str = None,
323367
method: str = None,
324368
headers: Dict[str, str] = None,
325-
postData: Union[str, bytes] = None,
369+
postData: Union[Any, str, bytes] = None,
326370
) -> None:
327371
overrides = cast(FallbackOverrideParameters, locals_to_params(locals()))
328372
self._check_not_handled()
@@ -335,23 +379,18 @@ def _internal_continue(
335379
) -> Coroutine[Any, Any, None]:
336380
async def continue_route() -> None:
337381
try:
338-
post_data_for_wire: Optional[str] = None
339-
post_data_from_overrides = self.request._fallback_overrides.get(
340-
"postData"
341-
)
342-
if post_data_from_overrides is not None:
343-
post_data_for_wire = (
344-
base64.b64encode(post_data_from_overrides.encode()).decode()
345-
if isinstance(post_data_from_overrides, str)
346-
else base64.b64encode(post_data_from_overrides).decode()
347-
)
348-
params = locals_to_params(
349-
cast(Dict[str, str], self.request._fallback_overrides)
350-
)
382+
params: Dict[str, Any] = {}
383+
params["url"] = self.request._fallback_overrides.url
384+
params["method"] = self.request._fallback_overrides.method
385+
params["headers"] = self.request._fallback_overrides.headers
386+
if self.request._fallback_overrides.post_data_buffer is not None:
387+
params["postData"] = base64.b64encode(
388+
self.request._fallback_overrides.post_data_buffer
389+
).decode()
390+
params = locals_to_params(params)
391+
351392
if "headers" in params:
352393
params["headers"] = serialize_headers(params["headers"])
353-
if post_data_for_wire is not None:
354-
params["postData"] = post_data_for_wire
355394
await self._connection.wrap_api_call(
356395
lambda: self._race_with_page_close(
357396
self._channel.send(

playwright/_impl/_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ def get_by_role(
788788
exact=exact,
789789
)
790790

791-
def get_by_test_id(self, testId: str) -> "Locator":
791+
def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator":
792792
return self._main_frame.get_by_test_id(testId)
793793

794794
def get_by_text(

0 commit comments

Comments
 (0)