Skip to content

Commit 3ffc9a9

Browse files
authored
chore(roll): roll to 1.31.0-beta-1676906983000 (microsoft#1775)
1 parent 405de18 commit 3ffc9a9

25 files changed

+776
-95
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 -->110.0.5481.38<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->111.0.5563.19<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->108.0.2<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->109.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_api_structures.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class ExpectedTextValue(TypedDict, total=False):
185185
class FrameExpectOptions(TypedDict, total=False):
186186
expressionArg: Any
187187
expectedText: Optional[List[ExpectedTextValue]]
188-
expectedNumber: Optional[int]
188+
expectedNumber: Optional[float]
189189
expectedValue: Optional[Any]
190190
useInnerText: Optional[bool]
191191
isNot: bool

playwright/_impl/_assertions.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,25 @@ async def not_to_be_focused(
618618
__tracebackhide__ = True
619619
await self._not.to_be_focused(timeout)
620620

621+
async def to_be_in_viewport(
622+
self,
623+
ratio: float = None,
624+
timeout: float = None,
625+
) -> None:
626+
__tracebackhide__ = True
627+
await self._expect_impl(
628+
"to.be.in.viewport",
629+
FrameExpectOptions(timeout=timeout, expectedNumber=ratio),
630+
None,
631+
"Locator expected to be in viewport",
632+
)
633+
634+
async def not_to_be_in_viewport(
635+
self, ratio: float = None, timeout: float = None
636+
) -> None:
637+
__tracebackhide__ = True
638+
await self._not.to_be_in_viewport(ratio=ratio, timeout=timeout)
639+
621640

622641
class APIResponseAssertions:
623642
def __init__(self, response: APIResponse, is_not: bool = False) -> None:

playwright/_impl/_browser_context.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ async def _on_route(self, route: Route) -> None:
191191
handled = await route_handler.handle(route)
192192
finally:
193193
if len(self._routes) == 0:
194-
asyncio.create_task(self._disable_interception())
194+
asyncio.create_task(
195+
self._connection.wrap_api_call(
196+
lambda: self._update_interception_patterns(), True
197+
)
198+
)
195199
if handled:
196200
return
197201
await route._internal_continue(is_internal=True)
@@ -304,10 +308,7 @@ async def route(
304308
times,
305309
),
306310
)
307-
if len(self._routes) == 1:
308-
await self._channel.send(
309-
"setNetworkInterceptionEnabled", dict(enabled=True)
310-
)
311+
await self._update_interception_patterns()
311312

312313
async def unroute(
313314
self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None
@@ -318,8 +319,7 @@ async def unroute(
318319
self._routes,
319320
)
320321
)
321-
if len(self._routes) == 0:
322-
await self._disable_interception()
322+
await self._update_interception_patterns()
323323

324324
async def _record_into_har(
325325
self,
@@ -360,8 +360,11 @@ async def route_from_har(
360360
)
361361
await router.add_context_route(self)
362362

363-
async def _disable_interception(self) -> None:
364-
await self._channel.send("setNetworkInterceptionEnabled", dict(enabled=False))
363+
async def _update_interception_patterns(self) -> None:
364+
patterns = RouteHandler.prepare_interception_patterns(self._routes)
365+
await self._channel.send(
366+
"setNetworkInterceptionPatterns", {"patterns": patterns}
367+
)
365368

366369
def expect_event(
367370
self,

playwright/_impl/_helper.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,28 @@ def impl() -> None:
291291
def will_expire(self) -> bool:
292292
return self._handled_count + 1 >= self._times
293293

294+
@staticmethod
295+
def prepare_interception_patterns(
296+
handlers: List["RouteHandler"],
297+
) -> List[Dict[str, str]]:
298+
patterns = []
299+
all = False
300+
for handler in handlers:
301+
if isinstance(handler.matcher.match, str):
302+
patterns.append({"glob": handler.matcher.match})
303+
elif isinstance(handler.matcher._regex_obj, re.Pattern):
304+
patterns.append(
305+
{
306+
"regexSource": handler.matcher._regex_obj.pattern,
307+
"regexFlags": escape_regex_flags(handler.matcher._regex_obj),
308+
}
309+
)
310+
else:
311+
all = True
312+
if all:
313+
return [{"glob": "**/*"}]
314+
return patterns
315+
294316

295317
def is_safe_close_error(error: Exception) -> bool:
296318
message = str(error)

playwright/_impl/_locator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ async def _expect(
643643
{
644644
"selector": self._selector,
645645
"expression": expression,
646-
**options,
646+
**({k: v for k, v in options.items() if v is not None}),
647647
},
648648
)
649649
if result.get("received"):

playwright/_impl/_network.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ async def _actual_headers(self) -> "RawHeaders":
241241
self._all_headers_future.set_result(RawHeaders(headers))
242242
return await self._all_headers_future
243243

244+
def _target_closed_future(self) -> asyncio.Future:
245+
if not hasattr(self.frame, "_page"):
246+
return asyncio.Future()
247+
return self.frame._page._closed_or_crashed_future
248+
244249

245250
class Route(ChannelOwner):
246251
def __init__(
@@ -348,10 +353,11 @@ async def fetch(
348353
method: str = None,
349354
headers: Dict[str, str] = None,
350355
postData: Union[Any, str, bytes] = None,
356+
maxRedirects: int = None,
351357
) -> "APIResponse":
352358
page = self.request.frame._page
353359
return await page.context.request._inner_fetch(
354-
self.request, url, method, headers, postData
360+
self.request, url, method, headers, postData, maxRedirects=maxRedirects
355361
)
356362

357363
async def fallback(
@@ -419,30 +425,22 @@ async def _redirected_navigation_request(self, url: str) -> None:
419425
self._report_handled(True)
420426

421427
async def _race_with_page_close(self, future: Coroutine) -> None:
422-
if hasattr(self.request.frame, "_page"):
423-
page = self.request.frame._page
424-
# When page closes or crashes, we catch any potential rejects from this Route.
425-
# Note that page could be missing when routing popup's initial request that
426-
# does not have a Page initialized just yet.
427-
fut = asyncio.create_task(future)
428-
# Rewrite the user's stack to the new task which runs in the background.
429-
setattr(
430-
fut,
431-
"__pw_stack__",
432-
getattr(
433-
asyncio.current_task(self._loop), "__pw_stack__", inspect.stack()
434-
),
435-
)
436-
await asyncio.wait(
437-
[fut, page._closed_or_crashed_future],
438-
return_when=asyncio.FIRST_COMPLETED,
439-
)
440-
if fut.done() and fut.exception():
441-
raise cast(BaseException, fut.exception())
442-
if page._closed_or_crashed_future.done():
443-
await asyncio.gather(fut, return_exceptions=True)
444-
else:
445-
await future
428+
fut = asyncio.create_task(future)
429+
# Rewrite the user's stack to the new task which runs in the background.
430+
setattr(
431+
fut,
432+
"__pw_stack__",
433+
getattr(asyncio.current_task(self._loop), "__pw_stack__", inspect.stack()),
434+
)
435+
target_closed_future = self.request._target_closed_future()
436+
await asyncio.wait(
437+
[fut, target_closed_future],
438+
return_when=asyncio.FIRST_COMPLETED,
439+
)
440+
if fut.done() and fut.exception():
441+
raise cast(BaseException, fut.exception())
442+
if target_closed_future.done():
443+
await asyncio.gather(fut, return_exceptions=True)
446444

447445

448446
class Response(ChannelOwner):
@@ -522,7 +520,20 @@ async def security_details(self) -> Optional[SecurityDetails]:
522520
return await self._channel.send("securityDetails")
523521

524522
async def finished(self) -> None:
525-
await self._finished_future
523+
async def on_finished() -> None:
524+
await self._request._target_closed_future()
525+
raise Error("Target closed")
526+
527+
on_finished_task = asyncio.create_task(on_finished())
528+
await asyncio.wait(
529+
cast(
530+
List[Union[asyncio.Task, asyncio.Future]],
531+
[self._finished_future, on_finished_task],
532+
),
533+
return_when=asyncio.FIRST_COMPLETED,
534+
)
535+
if on_finished_task.done():
536+
await on_finished_task
526537

527538
async def body(self) -> bytes:
528539
binary = await self._channel.send("body")

playwright/_impl/_page.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,11 @@ async def _on_route(self, route: Route) -> None:
254254
handled = await route_handler.handle(route)
255255
finally:
256256
if len(self._routes) == 0:
257-
asyncio.create_task(self._disable_interception())
257+
asyncio.create_task(
258+
self._connection.wrap_api_call(
259+
lambda: self._update_interception_patterns(), True
260+
)
261+
)
258262
if handled:
259263
return
260264
await self._browser_context._on_route(route)
@@ -594,10 +598,7 @@ async def route(
594598
times,
595599
),
596600
)
597-
if len(self._routes) == 1:
598-
await self._channel.send(
599-
"setNetworkInterceptionEnabled", dict(enabled=True)
600-
)
601+
await self._update_interception_patterns()
601602

602603
async def unroute(
603604
self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None
@@ -608,8 +609,7 @@ async def unroute(
608609
self._routes,
609610
)
610611
)
611-
if len(self._routes) == 0:
612-
await self._disable_interception()
612+
await self._update_interception_patterns()
613613

614614
async def route_from_har(
615615
self,
@@ -629,8 +629,11 @@ async def route_from_har(
629629
)
630630
await router.add_page_route(self)
631631

632-
async def _disable_interception(self) -> None:
633-
await self._channel.send("setNetworkInterceptionEnabled", dict(enabled=False))
632+
async def _update_interception_patterns(self) -> None:
633+
patterns = RouteHandler.prepare_interception_patterns(self._routes)
634+
await self._channel.send(
635+
"setNetworkInterceptionPatterns", {"patterns": patterns}
636+
)
634637

635638
async def screenshot(
636639
self,

playwright/_impl/_str_utils.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,13 @@ def escape_for_text_selector(
4848

4949

5050
def escape_for_attribute_selector(value: str, exact: bool = None) -> str:
51-
suffix = "" if exact else "i"
52-
return '"' + value.replace('"', '\\"') + '"' + suffix
51+
# TODO: this should actually be
52+
# cssEscape(value).replace(/\\ /g, ' ')
53+
# However, our attribute selectors do not conform to CSS parsing spec,
54+
# so we escape them differently.
55+
return (
56+
'"'
57+
+ value.replace("\\", "\\\\").replace('"', '\\"')
58+
+ '"'
59+
+ ("s" if exact else "i")
60+
)

0 commit comments

Comments
 (0)