Skip to content

Commit 1a85b74

Browse files
committed
In progress changes from v1.53.0 release
1 parent fe34e0f commit 1a85b74

File tree

10 files changed

+183
-176
lines changed

10 files changed

+183
-176
lines changed

playwright/_impl/_assertions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ async def _expect_impl(
5151
expect_options: FrameExpectOptions,
5252
expected: Any,
5353
message: str,
54+
# title: str,
5455
) -> None:
5556
__tracebackhide__ = True
5657
expect_options["isNot"] = self._is_not
@@ -60,6 +61,7 @@ async def _expect_impl(
6061
message = message.replace("expected to", "expected not to")
6162
if "useInnerText" in expect_options and expect_options["useInnerText"] is None:
6263
del expect_options["useInnerText"]
64+
# result = await self._actual_locator._expect(expression, expect_options, title)
6365
result = await self._actual_locator._expect(expression, expect_options)
6466
if result["matches"] == self._is_not:
6567
actual = result.get("received")

playwright/_impl/_browser.py

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

15-
import json
1615
from pathlib import Path
1716
from types import SimpleNamespace
1817
from typing import TYPE_CHECKING, Dict, List, Optional, Pattern, Sequence, Union, cast
@@ -38,12 +37,9 @@
3837
HarMode,
3938
ReducedMotion,
4039
ServiceWorkersPolicy,
41-
async_readfile,
4240
locals_to_params,
4341
make_dirs_for_file,
44-
prepare_record_har_options,
4542
)
46-
from playwright._impl._network import serialize_headers, to_client_certificates_protocol
4743
from playwright._impl._page import Page
4844

4945
if TYPE_CHECKING: # pragma: no cover
@@ -65,12 +61,41 @@ def __init__(
6561
self._cr_tracing_path: Optional[str] = None
6662

6763
self._contexts: List[BrowserContext] = []
64+
self._traces_dir: Optional[str] = None
65+
self._channel.on(
66+
"context",
67+
lambda context: self._did_create_context(cast(BrowserContext, context)),
68+
)
6869
self._channel.on("close", lambda _: self._on_close())
6970
self._close_reason: Optional[str] = None
7071

7172
def __repr__(self) -> str:
7273
return f"<Browser type={self._browser_type} version={self.version}>"
7374

75+
def connect_to_browser_type(
76+
self,
77+
browser_type: BrowserType,
78+
traces_dir: Optional[str] = None,
79+
) -> None:
80+
# Note: when using connect(), `browserType` is different from `this.parent`.
81+
# This is why browser type is not wired up in the constructor, and instead this separate method is called later on.
82+
self._browser_type = browser_type
83+
self._traces_dir = traces_dir
84+
for context in self._contexts:
85+
context._tracing._traces_dir = traces_dir
86+
browser_type._playwright.selectors._contextsForSelectors.append(context)
87+
88+
def _did_create_context(self, context: BrowserContext) -> None:
89+
context._browser = self
90+
self._contexts.append(context)
91+
# Note: when connecting to a browser, initial contexts arrive before `_browserType` is set,
92+
# and will be configured later in `ConnectToBrowserType`.
93+
if self._browser_type:
94+
context._tracing._traces_dir = self._traces_dir
95+
self._browser_type._playwright.selectors._contextsForSelectors.append(
96+
context
97+
)
98+
7499
def _on_close(self) -> None:
75100
self._is_connected = False
76101
self.emit(Browser.Events.Disconnected, self)
@@ -126,11 +151,19 @@ async def new_context(
126151
clientCertificates: List[ClientCertificate] = None,
127152
) -> BrowserContext:
128153
params = locals_to_params(locals())
129-
await prepare_browser_context_params(params)
154+
await self._browser_type.prepare_browser_context_params(params)
130155

131156
channel = await self._channel.send("newContext", params)
132157
context = cast(BrowserContext, from_channel(channel))
133-
self._browser_type._did_create_context(context, params, {})
158+
await context.initialize_har_from_options(
159+
{
160+
"recordHarPath": recordHarPath,
161+
"recordHarContent": recordHarContent,
162+
"recordHarOmitContent": recordHarOmitContent,
163+
"recordHarUrlFilter": recordHarUrlFilter,
164+
"recordHarMode": recordHarMode,
165+
}
166+
)
134167
return context
135168

136169
async def new_page(
@@ -181,6 +214,7 @@ async def inner() -> Page:
181214
context._owner_page = page
182215
return page
183216

217+
# TODO: Args
184218
return await self._connection.wrap_api_call(inner)
185219

186220
async def close(self, reason: str = None) -> None:
@@ -226,43 +260,3 @@ async def stop_tracing(self) -> bytes:
226260
f.write(buffer)
227261
self._cr_tracing_path = None
228262
return buffer
229-
230-
231-
async def prepare_browser_context_params(params: Dict) -> None:
232-
if params.get("noViewport"):
233-
del params["noViewport"]
234-
params["noDefaultViewport"] = True
235-
if "defaultBrowserType" in params:
236-
del params["defaultBrowserType"]
237-
if "extraHTTPHeaders" in params:
238-
params["extraHTTPHeaders"] = serialize_headers(params["extraHTTPHeaders"])
239-
if "recordHarPath" in params:
240-
params["recordHar"] = prepare_record_har_options(params)
241-
del params["recordHarPath"]
242-
if "recordVideoDir" in params:
243-
params["recordVideo"] = {"dir": Path(params["recordVideoDir"]).absolute()}
244-
if "recordVideoSize" in params:
245-
params["recordVideo"]["size"] = params["recordVideoSize"]
246-
del params["recordVideoSize"]
247-
del params["recordVideoDir"]
248-
if "storageState" in params:
249-
storageState = params["storageState"]
250-
if not isinstance(storageState, dict):
251-
params["storageState"] = json.loads(
252-
(await async_readfile(storageState)).decode()
253-
)
254-
if params.get("colorScheme", None) == "null":
255-
params["colorScheme"] = "no-override"
256-
if params.get("reducedMotion", None) == "null":
257-
params["reducedMotion"] = "no-override"
258-
if params.get("forcedColors", None) == "null":
259-
params["forcedColors"] = "no-override"
260-
if params.get("contrast", None) == "null":
261-
params["contrast"] = "no-override"
262-
if "acceptDownloads" in params:
263-
params["acceptDownloads"] = "accept" if params["acceptDownloads"] else "deny"
264-
265-
if "clientCertificates" in params:
266-
params["clientCertificates"] = await to_client_certificates_protocol(
267-
params["clientCertificates"]
268-
)

playwright/_impl/_browser_context.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
async_writefile,
6767
locals_to_params,
6868
parse_error,
69-
prepare_record_har_options,
7069
to_impl,
7170
)
7271
from playwright._impl._network import (
@@ -106,6 +105,7 @@ def __init__(
106105
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
107106
) -> None:
108107
super().__init__(parent, type, guid, initializer)
108+
# Browser is null for browser contexts created outside of normal browser, e.g. android or electron.
109109
# circular import workaround:
110110
self._browser: Optional["Browser"] = None
111111
if parent.__class__.__name__ == "Browser":
@@ -220,7 +220,7 @@ def __init__(
220220
BrowserContext.Events.RequestFailed: "requestFailed",
221221
}
222222
)
223-
self._close_was_called = False
223+
self._closing_or_closed = False
224224

225225
def __repr__(self) -> str:
226226
return f"<BrowserContext browser={self.browser}>"
@@ -237,7 +237,7 @@ async def _on_route(self, route: Route) -> None:
237237
route_handlers = self._routes.copy()
238238
for route_handler in route_handlers:
239239
# If the page or the context was closed we stall all requests right away.
240-
if (page and page._close_was_called) or self._close_was_called:
240+
if (page and page._close_was_called) or self._closing_or_closed:
241241
return
242242
if not route_handler.matches(route.request.url):
243243
continue
@@ -310,14 +310,22 @@ def pages(self) -> List[Page]:
310310
def browser(self) -> Optional["Browser"]:
311311
return self._browser
312312

313-
def _set_options(self, context_options: Dict, browser_options: Dict) -> None:
314-
self._options = context_options
315-
if self._options.get("recordHar"):
316-
self._har_recorders[""] = {
317-
"path": self._options["recordHar"]["path"],
318-
"content": self._options["recordHar"].get("content"),
319-
}
320-
self._tracing._traces_dir = browser_options.get("tracesDir")
313+
async def initialize_har_from_options(self, options: Dict) -> None:
314+
record_har_path = str(options["recordHarPath"])
315+
if not record_har_path or len(record_har_path) == 0:
316+
return
317+
default_policy = "attach" if record_har_path.endswith(".zip") else "embed"
318+
content_policy = options.get(
319+
"recordHarContent",
320+
"omit" if options["recordHarOmitContent"] is True else default_policy,
321+
)
322+
await self._record_into_har(
323+
har=record_har_path,
324+
page=None,
325+
url=options["recordHarUrlFilter"],
326+
update_content=content_policy,
327+
update_mode=options.get("recordHarMode", "full"),
328+
)
321329

322330
async def new_page(self) -> Page:
323331
if self._owner_page:
@@ -476,22 +484,25 @@ async def _record_into_har(
476484
update_content: HarContentPolicy = None,
477485
update_mode: HarMode = None,
478486
) -> None:
487+
update_content = update_content or "attach"
479488
params: Dict[str, Any] = {
480-
"options": prepare_record_har_options(
481-
{
482-
"recordHarPath": har,
483-
"recordHarContent": update_content or "attach",
484-
"recordHarMode": update_mode or "minimal",
485-
"recordHarUrlFilter": url,
486-
}
487-
)
489+
"options": {
490+
"zip": str(har).endswith(".zip"),
491+
"content": update_content,
492+
"urlGlob": url if isinstance(url, str) else None,
493+
"urlRegexSource": url.pattern if isinstance(url, Pattern) else None,
494+
"urlRegexFlags": (
495+
escape_regex_flags(url) if isinstance(url, Pattern) else None
496+
),
497+
"mode": update_mode or "minimal",
498+
}
488499
}
489500
if page:
490501
params["page"] = page._channel
491502
har_id = await self._channel.send("harStart", params)
492503
self._har_recorders[har_id] = {
493504
"path": str(har),
494-
"content": update_content or "attach",
505+
"content": update_content,
495506
}
496507

497508
async def route_from_har(
@@ -557,20 +568,21 @@ def expect_event(
557568
def _on_close(self) -> None:
558569
if self._browser:
559570
self._browser._contexts.remove(self)
571+
self._browser._browser_type._playwright.selectors._contextsForSelectors.remove(
572+
self
573+
)
560574

561575
self._dispose_har_routers()
562576
self._tracing._reset_stack_counter()
563577
self.emit(BrowserContext.Events.Close, self)
564578

565579
async def close(self, reason: str = None) -> None:
566-
if self._close_was_called:
580+
if self._closing_or_closed:
567581
return
568582
self._close_reason = reason
569-
self._close_was_called = True
583+
self._closing_or_closed = True
570584

571-
await self._channel._connection.wrap_api_call(
572-
lambda: self.request.dispose(reason=reason), True
573-
)
585+
await self.request.dispose(reason=reason)
574586

575587
async def _inner_close() -> None:
576588
for har_id, params in self._har_recorders.items():

0 commit comments

Comments
 (0)