From 36ff52e8ff7743664f8e01f338ae05892ee25fba Mon Sep 17 00:00:00 2001 From: Ross Wollman Date: Wed, 29 Jun 2022 17:21:36 -0700 Subject: [PATCH] chore: port update option for routeFromHar (#1392) Fixes #1389. Ports: - [x] https://github.com/microsoft/playwright/commit/6a8d835145e2f4002ee00b67a80a1f70af956703 (chore: allow updating har while routing (#15197)) --- playwright/_impl/_browser.py | 39 +++-------- playwright/_impl/_browser_context.py | 75 +++++++++++++++++++-- playwright/_impl/_browser_type.py | 4 ++ playwright/_impl/_helper.py | 35 ++++++++++ playwright/_impl/_local_utils.py | 4 ++ playwright/_impl/_page.py | 6 +- playwright/async_api/_generated.py | 22 ++++--- playwright/sync_api/_generated.py | 22 ++++--- setup.py | 2 +- tests/async/test_har.py | 97 ++++++++++++++++++++++++++++ tests/sync/test_har.py | 89 +++++++++++++++++++++++++ 11 files changed, 340 insertions(+), 55 deletions(-) diff --git a/playwright/_impl/_browser.py b/playwright/_impl/_browser.py index 4f5a72878..3cca571aa 100644 --- a/playwright/_impl/_browser.py +++ b/playwright/_impl/_browser.py @@ -16,7 +16,7 @@ import json from pathlib import Path from types import SimpleNamespace -from typing import TYPE_CHECKING, Any, Dict, List, Pattern, Union, cast +from typing import TYPE_CHECKING, Dict, List, Pattern, Union, cast from playwright._impl._api_structures import ( Geolocation, @@ -38,11 +38,10 @@ async_readfile, is_safe_close_error, locals_to_params, + prepare_record_har_options, ) -from playwright._impl._local_utils import LocalUtils from playwright._impl._network import serialize_headers from playwright._impl._page import Page -from playwright._impl._str_utils import escape_regex_flags if TYPE_CHECKING: # pragma: no cover from playwright._impl._browser_type import BrowserType @@ -64,7 +63,6 @@ def __init__( self._should_close_connection_on_close = False self._contexts: List[BrowserContext] = [] - _utils: LocalUtils self._channel.on("close", lambda _: self._on_close()) def __repr__(self) -> str: @@ -131,6 +129,7 @@ async def new_context( self._contexts.append(context) context._browser = self context._options = params + context._set_browser_type(self._browser_type) return context async def new_page( @@ -177,6 +176,11 @@ async def new_page( context._owner_page = page return page + def _set_browser_type(self, browser_type: "BrowserType") -> None: + self._browser_type = browser_type + for context in self._contexts: + context._set_browser_type(browser_type) + async def close(self) -> None: if self._is_closed_or_closing: return @@ -224,32 +228,7 @@ async def normalize_context_params(is_sync: bool, params: Dict) -> None: if "extraHTTPHeaders" in params: params["extraHTTPHeaders"] = serialize_headers(params["extraHTTPHeaders"]) if "recordHarPath" in params: - recordHar: Dict[str, Any] = {"path": str(params["recordHarPath"])} - params["recordHar"] = recordHar - if "recordHarUrlFilter" in params: - opt = params["recordHarUrlFilter"] - if isinstance(opt, str): - params["recordHar"]["urlGlob"] = opt - if isinstance(opt, Pattern): - params["recordHar"]["urlRegexSource"] = opt.pattern - params["recordHar"]["urlRegexFlags"] = escape_regex_flags(opt) - del params["recordHarUrlFilter"] - if "recordHarMode" in params: - params["recordHar"]["mode"] = params["recordHarMode"] - del params["recordHarMode"] - - new_content_api = None - old_content_api = None - if "recordHarContent" in params: - new_content_api = params["recordHarContent"] - del params["recordHarContent"] - if "recordHarOmitContent" in params: - old_content_api = params["recordHarOmitContent"] - del params["recordHarOmitContent"] - content = new_content_api or ("omit" if old_content_api else None) - if content: - params["recordHar"]["content"] = content - + params["recordHar"] = prepare_record_har_options(params) del params["recordHarPath"] if "recordVideoDir" in params: params["recordVideo"] = {"dir": str(params["recordVideoDir"])} diff --git a/playwright/_impl/_browser_context.py b/playwright/_impl/_browser_context.py index d8b172810..844955322 100644 --- a/playwright/_impl/_browser_context.py +++ b/playwright/_impl/_browser_context.py @@ -16,7 +16,18 @@ import json from pathlib import Path from types import SimpleNamespace -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Pattern, + Set, + Union, + cast, +) from playwright._impl._api_structures import ( Cookie, @@ -37,6 +48,7 @@ from playwright._impl._frame import Frame from playwright._impl._har_router import HarRouter from playwright._impl._helper import ( + HarRecordingMetadata, RouteFromHarNotFoundPolicy, RouteHandler, RouteHandlerCallback, @@ -47,6 +59,7 @@ async_writefile, is_safe_close_error, locals_to_params, + prepare_record_har_options, to_impl, ) from playwright._impl._network import Request, Response, Route, serialize_headers @@ -56,6 +69,7 @@ if TYPE_CHECKING: # pragma: no cover from playwright._impl._browser import Browser + from playwright._impl._browser_type import BrowserType class BrowserContext(ChannelOwner): @@ -85,6 +99,7 @@ def __init__( self._background_pages: Set[Page] = set() self._service_workers: Set[Worker] = set() self._tracing = cast(Tracing, from_channel(initializer["tracing"])) + self._har_recorders: Dict[str, HarRecordingMetadata] = {} self._request: APIRequestContext = from_channel( initializer["APIRequestContext"] ) @@ -201,6 +216,14 @@ def pages(self) -> List[Page]: def browser(self) -> Optional["Browser"]: return self._browser + def _set_browser_type(self, browser_type: "BrowserType") -> None: + self._browser_type = browser_type + if self._options.get("recordHar"): + self._har_recorders[""] = { + "path": self._options["recordHar"]["path"], + "content": self._options["recordHar"].get("content"), + } + async def new_page(self) -> Page: if self._owner_page: raise Error("Please use browser.new_context()") @@ -294,12 +317,37 @@ async def unroute( if len(self._routes) == 0: await self._disable_interception() + async def _record_into_har( + self, + har: Union[Path, str], + page: Optional[Page] = None, + url: Union[Pattern, str] = None, + ) -> None: + params = { + "options": prepare_record_har_options( + { + "recordHarPath": har, + "recordHarContent": "attach", + "recordHarMode": "minimal", + "recordHarUrlFilter": url, + } + ) + } + if page: + params["page"] = page._channel + har_id = await self._channel.send("harStart", params) + self._har_recorders[har_id] = {"path": str(har), "content": "attach"} + async def route_from_har( self, har: Union[Path, str], - url: URLMatch = None, + url: Union[Pattern, str] = None, not_found: RouteFromHarNotFoundPolicy = None, + update: bool = None, ) -> None: + if update: + await self._record_into_har(har=har, page=None, url=url) + return router = await HarRouter.create( local_utils=self._connection.local_utils, file=str(har), @@ -338,13 +386,26 @@ def _on_close(self) -> None: async def close(self) -> None: try: - if self._options.get("recordHar"): + for har_id, params in self._har_recorders.items(): har = cast( - Artifact, from_channel(await self._channel.send("harExport")) - ) - await har.save_as( - cast(Dict[str, str], self._options["recordHar"])["path"] + Artifact, + from_channel( + await self._channel.send("harExport", {"harId": har_id}) + ), ) + # Server side will compress artifact if content is attach or if file is .zip. + is_compressed = params.get("content") == "attach" or params[ + "path" + ].endswith(".zip") + need_compressed = params["path"].endswith(".zip") + if is_compressed and not need_compressed: + tmp_path = params["path"] + ".tmp" + await har.save_as(tmp_path) + await self._connection.local_utils.har_unzip( + zipFile=tmp_path, harFile=params["path"] + ) + else: + await har.save_as(params["path"]) await har.delete() await self._channel.send("close") await self._closed_future diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index 761fb57c9..a1c297080 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -92,6 +92,7 @@ async def launch( browser = cast( Browser, from_channel(await self._channel.send("launch", params)) ) + browser._set_browser_type(self) return browser async def launch_persistent_context( @@ -154,6 +155,7 @@ async def launch_persistent_context( from_channel(await self._channel.send("launchPersistentContext", params)), ) context._options = params + context._set_browser_type(self) return context async def connect_over_cdp( @@ -174,6 +176,7 @@ async def connect_over_cdp( if default_context: browser._contexts.append(default_context) default_context._browser = browser + browser._set_browser_type(self) return browser async def connect( @@ -230,6 +233,7 @@ def handle_transport_close() -> None: transport.once("close", handle_transport_close) + browser._set_browser_type(self) return browser diff --git a/playwright/_impl/_helper.py b/playwright/_impl/_helper.py index faa3de398..fb2295298 100644 --- a/playwright/_impl/_helper.py +++ b/playwright/_impl/_helper.py @@ -41,6 +41,7 @@ from playwright._impl._api_structures import NameValue from playwright._impl._api_types import Error, TimeoutError +from playwright._impl._str_utils import escape_regex_flags if sys.version_info >= (3, 8): # pragma: no cover from typing import Literal, TypedDict @@ -85,6 +86,40 @@ class FallbackOverrideParameters(TypedDict, total=False): postData: Optional[Union[str, bytes]] +class HarRecordingMetadata(TypedDict, total=False): + path: str + content: Optional[HarContentPolicy] + + +def prepare_record_har_options(params: Dict) -> Dict[str, Any]: + out_params: Dict[str, Any] = {"path": str(params["recordHarPath"])} + if "recordHarUrlFilter" in params: + opt = params["recordHarUrlFilter"] + if isinstance(opt, str): + out_params["urlGlob"] = opt + if isinstance(opt, Pattern): + out_params["urlRegexSource"] = opt.pattern + out_params["urlRegexFlags"] = escape_regex_flags(opt) + del params["recordHarUrlFilter"] + if "recordHarMode" in params: + out_params["mode"] = params["recordHarMode"] + del params["recordHarMode"] + + new_content_api = None + old_content_api = None + if "recordHarContent" in params: + new_content_api = params["recordHarContent"] + del params["recordHarContent"] + if "recordHarOmitContent" in params: + old_content_api = params["recordHarOmitContent"] + del params["recordHarOmitContent"] + content = new_content_api or ("omit" if old_content_api else None) + if content: + out_params["content"] = content + + return out_params + + class ParsedMessageParams(TypedDict): type: str guid: str diff --git a/playwright/_impl/_local_utils.py b/playwright/_impl/_local_utils.py index a9d9395cc..73870ae64 100644 --- a/playwright/_impl/_local_utils.py +++ b/playwright/_impl/_local_utils.py @@ -53,3 +53,7 @@ async def har_lookup( async def har_close(self, harId: str) -> None: params = locals_to_params(locals()) await self._channel.send("harClose", params) + + async def har_unzip(self, zipFile: str, harFile: str) -> None: + params = locals_to_params(locals()) + await self._channel.send("harUnzip", params) diff --git a/playwright/_impl/_page.py b/playwright/_impl/_page.py index c8066e496..ddb41aa12 100644 --- a/playwright/_impl/_page.py +++ b/playwright/_impl/_page.py @@ -605,9 +605,13 @@ async def unroute( async def route_from_har( self, har: Union[Path, str], - url: URLMatch = None, + url: Union[Pattern, str] = None, not_found: RouteFromHarNotFoundPolicy = None, + update: bool = None, ) -> None: + if update: + await self._browser_context._record_into_har(har=har, page=self, url=url) + return router = await HarRouter.create( local_utils=self._connection.local_utils, file=str(har), diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 2af4a9a60..f5e8f3d08 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -7744,8 +7744,9 @@ async def route_from_har( self, har: typing.Union[pathlib.Path, str], *, - url: typing.Union[str, typing.Pattern, typing.Callable[[str], bool]] = None, - not_found: Literal["abort", "fallback"] = None + url: typing.Union[str, typing.Pattern] = None, + not_found: Literal["abort", "fallback"] = None, + update: bool = None ) -> NoneType: """Page.route_from_har @@ -7761,7 +7762,7 @@ async def route_from_har( har : Union[pathlib.Path, str] Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory. - url : Union[Callable[[str], bool], Pattern, str, NoneType] + url : Union[Pattern, str, NoneType] A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file. not_found : Union["abort", "fallback", NoneType] @@ -7769,11 +7770,13 @@ async def route_from_har( - If set to 'fallback' missing requests will be sent to the network. Defaults to abort. + update : Union[bool, NoneType] + If specified, updates the given HAR with the actual network information instead of serving from file. """ return mapping.from_maybe_impl( await self._impl_obj.route_from_har( - har=har, url=self._wrap_handler(url), not_found=not_found + har=har, url=url, not_found=not_found, update=update ) ) @@ -10477,8 +10480,9 @@ async def route_from_har( self, har: typing.Union[pathlib.Path, str], *, - url: typing.Union[str, typing.Pattern, typing.Callable[[str], bool]] = None, - not_found: Literal["abort", "fallback"] = None + url: typing.Union[str, typing.Pattern] = None, + not_found: Literal["abort", "fallback"] = None, + update: bool = None ) -> NoneType: """BrowserContext.route_from_har @@ -10494,7 +10498,7 @@ async def route_from_har( har : Union[pathlib.Path, str] Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory. - url : Union[Callable[[str], bool], Pattern, str, NoneType] + url : Union[Pattern, str, NoneType] A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file. not_found : Union["abort", "fallback", NoneType] @@ -10502,11 +10506,13 @@ async def route_from_har( - If set to 'fallback' falls through to the next route handler in the handler chain. Defaults to abort. + update : Union[bool, NoneType] + If specified, updates the given HAR with the actual network information instead of serving from file. """ return mapping.from_maybe_impl( await self._impl_obj.route_from_har( - har=har, url=self._wrap_handler(url), not_found=not_found + har=har, url=url, not_found=not_found, update=update ) ) diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 800816068..14b5e4891 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -7770,8 +7770,9 @@ def route_from_har( self, har: typing.Union[pathlib.Path, str], *, - url: typing.Union[str, typing.Pattern, typing.Callable[[str], bool]] = None, - not_found: Literal["abort", "fallback"] = None + url: typing.Union[str, typing.Pattern] = None, + not_found: Literal["abort", "fallback"] = None, + update: bool = None ) -> NoneType: """Page.route_from_har @@ -7787,7 +7788,7 @@ def route_from_har( har : Union[pathlib.Path, str] Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory. - url : Union[Callable[[str], bool], Pattern, str, NoneType] + url : Union[Pattern, str, NoneType] A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file. not_found : Union["abort", "fallback", NoneType] @@ -7795,12 +7796,14 @@ def route_from_har( - If set to 'fallback' missing requests will be sent to the network. Defaults to abort. + update : Union[bool, NoneType] + If specified, updates the given HAR with the actual network information instead of serving from file. """ return mapping.from_maybe_impl( self._sync( self._impl_obj.route_from_har( - har=har, url=self._wrap_handler(url), not_found=not_found + har=har, url=url, not_found=not_found, update=update ) ) ) @@ -10503,8 +10506,9 @@ def route_from_har( self, har: typing.Union[pathlib.Path, str], *, - url: typing.Union[str, typing.Pattern, typing.Callable[[str], bool]] = None, - not_found: Literal["abort", "fallback"] = None + url: typing.Union[str, typing.Pattern] = None, + not_found: Literal["abort", "fallback"] = None, + update: bool = None ) -> NoneType: """BrowserContext.route_from_har @@ -10520,7 +10524,7 @@ def route_from_har( har : Union[pathlib.Path, str] Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a relative path, then it is resolved relative to the current working directory. - url : Union[Callable[[str], bool], Pattern, str, NoneType] + url : Union[Pattern, str, NoneType] A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern will be surved from the HAR file. If not specified, all requests are served from the HAR file. not_found : Union["abort", "fallback", NoneType] @@ -10528,12 +10532,14 @@ def route_from_har( - If set to 'fallback' falls through to the next route handler in the handler chain. Defaults to abort. + update : Union[bool, NoneType] + If specified, updates the given HAR with the actual network information instead of serving from file. """ return mapping.from_maybe_impl( self._sync( self._impl_obj.route_from_har( - har=har, url=self._wrap_handler(url), not_found=not_found + har=har, url=url, not_found=not_found, update=update ) ) ) diff --git a/setup.py b/setup.py index 5953e9960..b6e94c2b1 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.23.0" +driver_version = "1.23.0-beta-1656457055000" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_har.py b/tests/async/test_har.py index 65c2cb395..55c28e91a 100644 --- a/tests/async/test_har.py +++ b/tests/async/test_har.py @@ -577,3 +577,100 @@ async def test_should_disambiguate_by_header( assert await page_2.evaluate(fetch_function, "baz2") == "baz2" assert await page_2.evaluate(fetch_function, "baz3") == "baz3" assert await page_2.evaluate(fetch_function, "baz4") == "baz1" + + +async def test_should_produce_extracted_zip( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.har" + context = await browser.new_context( + record_har_mode="minimal", record_har_path=har_path, record_har_content="attach" + ) + page_1 = await context.new_page() + await page_1.goto(server.PREFIX + "/one-style.html") + await context.close() + + assert har_path.exists() + with har_path.open() as r: + content = r.read() + assert "log" in content + assert "background-color" not in r.read() + + context_2 = await browser.new_context() + await context_2.route_from_har(har_path, not_found="abort") + page_2 = await context_2.new_page() + await page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in await page_2.content() + await expect(page_2.locator("body")).to_have_css( + "background-color", "rgb(255, 192, 203)" + ) + + +async def test_should_update_har_zip_for_context( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.zip" + context = await browser.new_context() + await context.route_from_har(har_path, update=True) + page_1 = await context.new_page() + await page_1.goto(server.PREFIX + "/one-style.html") + await context.close() + + assert har_path.exists() + + context_2 = await browser.new_context() + await context_2.route_from_har(har_path, not_found="abort") + page_2 = await context_2.new_page() + await page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in await page_2.content() + await expect(page_2.locator("body")).to_have_css( + "background-color", "rgb(255, 192, 203)" + ) + + +async def test_should_update_har_zip_for_page( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.zip" + context = await browser.new_context() + page_1 = await context.new_page() + await page_1.route_from_har(har_path, update=True) + await page_1.goto(server.PREFIX + "/one-style.html") + await context.close() + + assert har_path.exists() + + context_2 = await browser.new_context() + page_2 = await context_2.new_page() + await page_2.route_from_har(har_path, not_found="abort") + await page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in await page_2.content() + await expect(page_2.locator("body")).to_have_css( + "background-color", "rgb(255, 192, 203)" + ) + + +async def test_should_update_extracted_har_zip_for_page( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.har" + context = await browser.new_context() + page_1 = await context.new_page() + await page_1.route_from_har(har_path, update=True) + await page_1.goto(server.PREFIX + "/one-style.html") + await context.close() + + assert har_path.exists() + with har_path.open() as r: + content = r.read() + assert "log" in content + assert "background-color" not in r.read() + + context_2 = await browser.new_context() + page_2 = await context_2.new_page() + await page_2.route_from_har(har_path, not_found="abort") + await page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in await page_2.content() + await expect(page_2.locator("body")).to_have_css( + "background-color", "rgb(255, 192, 203)" + ) diff --git a/tests/sync/test_har.py b/tests/sync/test_har.py index 0cb43be9b..f8eb26091 100644 --- a/tests/sync/test_har.py +++ b/tests/sync/test_har.py @@ -511,3 +511,92 @@ def test_should_disambiguate_by_header( assert page_2.evaluate(fetch_function, "baz2") == "baz2" assert page_2.evaluate(fetch_function, "baz3") == "baz3" assert page_2.evaluate(fetch_function, "baz4") == "baz1" + + +def test_should_produce_extracted_zip( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.har" + context = browser.new_context( + record_har_mode="minimal", record_har_path=har_path, record_har_content="attach" + ) + page_1 = context.new_page() + page_1.goto(server.PREFIX + "/one-style.html") + context.close() + + assert har_path.exists() + with har_path.open() as r: + content = r.read() + assert "log" in content + assert "background-color" not in r.read() + + context_2 = browser.new_context() + context_2.route_from_har(har_path, not_found="abort") + page_2 = context_2.new_page() + page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in page_2.content() + expect(page_2.locator("body")).to_have_css("background-color", "rgb(255, 192, 203)") + + +def test_should_update_har_zip_for_context( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.zip" + context = browser.new_context() + context.route_from_har(har_path, update=True) + page_1 = context.new_page() + page_1.goto(server.PREFIX + "/one-style.html") + context.close() + + assert har_path.exists() + + context_2 = browser.new_context() + context_2.route_from_har(har_path, not_found="abort") + page_2 = context_2.new_page() + page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in page_2.content() + expect(page_2.locator("body")).to_have_css("background-color", "rgb(255, 192, 203)") + + +def test_should_update_har_zip_for_page( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.zip" + context = browser.new_context() + page_1 = context.new_page() + page_1.route_from_har(har_path, update=True) + page_1.goto(server.PREFIX + "/one-style.html") + context.close() + + assert har_path.exists() + + context_2 = browser.new_context() + page_2 = context_2.new_page() + page_2.route_from_har(har_path, not_found="abort") + page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in page_2.content() + expect(page_2.locator("body")).to_have_css("background-color", "rgb(255, 192, 203)") + + +def test_should_update_extracted_har_zip_for_page( + browser: Browser, server: Server, assetdir: Path, tmpdir: Path +) -> None: + har_path = tmpdir / "har.har" + context = browser.new_context() + page_1 = context.new_page() + page_1.route_from_har(har_path, update=True) + page_1.goto(server.PREFIX + "/one-style.html") + context.close() + + assert har_path.exists() + with har_path.open() as r: + content = r.read() + assert "log" in content + assert "background-color" not in r.read() + + context_2 = browser.new_context() + page_2 = context_2.new_page() + page_2.route_from_har(har_path, not_found="abort") + page_2.goto(server.PREFIX + "/one-style.html") + assert "hello, world!" in page_2.content() + expect(page_2.locator("body")).to_have_css("background-color", "rgb(255, 192, 203)")