Skip to content

Commit d05857b

Browse files
authored
feat(har): allow saving HAR 1.2 for context (microsoft#264)
1 parent 3fb407e commit d05857b

File tree

12 files changed

+179
-4
lines changed

12 files changed

+179
-4
lines changed

playwright/async_api.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
MousePosition,
5050
PdfMargins,
5151
ProxyServer,
52+
RecordHarOptions,
5253
RequestFailure,
5354
ResourceTiming,
5455
SelectOption,
@@ -5820,6 +5821,7 @@ async def newContext(
58205821
defaultBrowserType: str = None,
58215822
videosPath: str = None,
58225823
videoSize: IntSize = None,
5824+
recordHar: RecordHarOptions = None,
58235825
) -> "BrowserContext":
58245826
"""Browser.newContext
58255827
@@ -5864,6 +5866,8 @@ async def newContext(
58645866
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `browserContext.close` for videos to be saved.
58655867
videoSize : Optional[{"width": int, "height": int}]
58665868
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
5869+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
5870+
Enables HAR recording for all pages into `har.path` file. If not specified, the HAR is not recorded. Make sure to await `browserContext.close` for the HAR to be saved.
58675871
58685872
Returns
58695873
-------
@@ -5891,6 +5895,7 @@ async def newContext(
58915895
defaultBrowserType=defaultBrowserType,
58925896
videosPath=videosPath,
58935897
videoSize=videoSize,
5898+
recordHar=recordHar,
58945899
)
58955900
)
58965901

@@ -5916,6 +5921,7 @@ async def newPage(
59165921
defaultBrowserType: str = None,
59175922
videosPath: str = None,
59185923
videoSize: IntSize = None,
5924+
recordHar: RecordHarOptions = None,
59195925
) -> "Page":
59205926
"""Browser.newPage
59215927
@@ -5961,6 +5967,8 @@ async def newPage(
59615967
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `page.close` for videos to be saved.
59625968
videoSize : Optional[{"width": int, "height": int}]
59635969
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
5970+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
5971+
Enables HAR recording for all pages into `har.path` file. If not specified, the HAR is not recorded. Make sure to await `page.close` for the HAR to be saved.
59645972
59655973
Returns
59665974
-------
@@ -5988,6 +5996,7 @@ async def newPage(
59885996
defaultBrowserType=defaultBrowserType,
59895997
videosPath=videosPath,
59905998
videoSize=videoSize,
5999+
recordHar=recordHar,
59916000
)
59926001
)
59936002

@@ -6154,6 +6163,7 @@ async def launchPersistentContext(
61546163
chromiumSandbox: bool = None,
61556164
videosPath: str = None,
61566165
videoSize: IntSize = None,
6166+
recordHar: RecordHarOptions = None,
61576167
) -> "BrowserContext":
61586168
"""BrowserType.launchPersistentContext
61596169
@@ -6228,6 +6238,8 @@ async def launchPersistentContext(
62286238
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `browserContext.close` for videos to be saved.
62296239
videoSize : Optional[{"width": int, "height": int}]
62306240
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
6241+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
6242+
Enables HAR recording for all the pages into `har.path` file. If not specified, HAR is not recorded. Make sure to await `page.close` for HAR to be saved.
62316243
62326244
Returns
62336245
-------
@@ -6270,6 +6282,7 @@ async def launchPersistentContext(
62706282
chromiumSandbox=chromiumSandbox,
62716283
videosPath=videosPath,
62726284
videoSize=videoSize,
6285+
recordHar=recordHar,
62736286
)
62746287
)
62756288

playwright/browser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Credentials,
2424
Geolocation,
2525
IntSize,
26+
RecordHarOptions,
2627
locals_to_params,
2728
)
2829
from playwright.network import serialize_headers
@@ -88,6 +89,7 @@ async def newContext(
8889
defaultBrowserType: str = None,
8990
videosPath: str = None,
9091
videoSize: IntSize = None,
92+
recordHar: RecordHarOptions = None,
9193
) -> BrowserContext:
9294
params = locals_to_params(locals())
9395
# Python is strict in which variables gets passed to methods. We get this
@@ -128,6 +130,7 @@ async def newPage(
128130
defaultBrowserType: str = None,
129131
videosPath: str = None,
130132
videoSize: IntSize = None,
133+
recordHar: RecordHarOptions = None,
131134
) -> Page:
132135
params = locals_to_params(locals())
133136
# Python is strict in which variables gets passed to methods. We get this

playwright/browser_type.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Geolocation,
2626
IntSize,
2727
ProxyServer,
28+
RecordHarOptions,
2829
locals_to_params,
2930
not_installed_error,
3031
)
@@ -108,6 +109,7 @@ async def launchPersistentContext(
108109
chromiumSandbox: bool = None,
109110
videosPath: str = None,
110111
videoSize: IntSize = None,
112+
recordHar: RecordHarOptions = None,
111113
) -> BrowserContext:
112114
userDataDir = str(Path(userDataDir))
113115
params = locals_to_params(locals())

playwright/helper.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ class PdfMargins(TypedDict):
185185
left: Optional[Union[str, int]]
186186

187187

188+
class RecordHarOptions(TypedDict):
189+
omitContent: Optional[bool]
190+
path: str
191+
192+
188193
DeviceDescriptor = TypedDict(
189194
"DeviceDescriptor",
190195
{

playwright/sync_api.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
MousePosition,
4949
PdfMargins,
5050
ProxyServer,
51+
RecordHarOptions,
5152
RequestFailure,
5253
ResourceTiming,
5354
SelectOption,
@@ -6056,6 +6057,7 @@ def newContext(
60566057
defaultBrowserType: str = None,
60576058
videosPath: str = None,
60586059
videoSize: IntSize = None,
6060+
recordHar: RecordHarOptions = None,
60596061
) -> "BrowserContext":
60606062
"""Browser.newContext
60616063
@@ -6100,6 +6102,8 @@ def newContext(
61006102
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `browserContext.close` for videos to be saved.
61016103
videoSize : Optional[{"width": int, "height": int}]
61026104
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
6105+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
6106+
Enables HAR recording for all pages into `har.path` file. If not specified, the HAR is not recorded. Make sure to await `browserContext.close` for the HAR to be saved.
61036107
61046108
Returns
61056109
-------
@@ -6128,6 +6132,7 @@ def newContext(
61286132
defaultBrowserType=defaultBrowserType,
61296133
videosPath=videosPath,
61306134
videoSize=videoSize,
6135+
recordHar=recordHar,
61316136
)
61326137
)
61336138
)
@@ -6154,6 +6159,7 @@ def newPage(
61546159
defaultBrowserType: str = None,
61556160
videosPath: str = None,
61566161
videoSize: IntSize = None,
6162+
recordHar: RecordHarOptions = None,
61576163
) -> "Page":
61586164
"""Browser.newPage
61596165
@@ -6199,6 +6205,8 @@ def newPage(
61996205
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `page.close` for videos to be saved.
62006206
videoSize : Optional[{"width": int, "height": int}]
62016207
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
6208+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
6209+
Enables HAR recording for all pages into `har.path` file. If not specified, the HAR is not recorded. Make sure to await `page.close` for the HAR to be saved.
62026210
62036211
Returns
62046212
-------
@@ -6227,6 +6235,7 @@ def newPage(
62276235
defaultBrowserType=defaultBrowserType,
62286236
videosPath=videosPath,
62296237
videoSize=videoSize,
6238+
recordHar=recordHar,
62306239
)
62316240
)
62326241
)
@@ -6396,6 +6405,7 @@ def launchPersistentContext(
63966405
chromiumSandbox: bool = None,
63976406
videosPath: str = None,
63986407
videoSize: IntSize = None,
6408+
recordHar: RecordHarOptions = None,
63996409
) -> "BrowserContext":
64006410
"""BrowserType.launchPersistentContext
64016411
@@ -6470,6 +6480,8 @@ def launchPersistentContext(
64706480
Enables video recording for all pages to `videosPath` folder. If not specified, videos are not recorded. Make sure to await `browserContext.close` for videos to be saved.
64716481
videoSize : Optional[{"width": int, "height": int}]
64726482
Specifies dimensions of the automatically recorded video. Can only be used if `videosPath` is set. If not specified the size will be equal to `viewport`. If `viewport` is not configured explicitly the video size defaults to 1280x720. Actual picture of the page will be scaled down if necessary to fit specified size.
6483+
recordHar : Optional[{"omitContent": Optional[bool], "path": str}]
6484+
Enables HAR recording for all the pages into `har.path` file. If not specified, HAR is not recorded. Make sure to await `page.close` for HAR to be saved.
64736485
64746486
Returns
64756487
-------
@@ -6513,6 +6525,7 @@ def launchPersistentContext(
65136525
chromiumSandbox=chromiumSandbox,
65146526
videosPath=videosPath,
65156527
videoSize=videoSize,
6528+
recordHar=recordHar,
65166529
)
65176530
)
65186531
)

scripts/expected_api_mismatch.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,4 @@ Parameter not implemented: BrowserContext.waitForEvent(optionsOrPredicate=)
121121
# 1.6 Todo
122122
Parameter not implemented: Browser.newPage(proxy=)
123123
Parameter not implemented: Browser.newContext(proxy=)
124-
Parameter not implemented: Browser.newPage(recordHar=)
125-
Parameter not implemented: Browser.newContext(recordHar=)
126124
Method not implemented: WebSocket.url
127-
Parameter not implemented: BrowserType.launchPersistentContext(recordHar=)

scripts/generate_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def return_value(value: Any) -> List[str]:
163163
from playwright.element_handle import ElementHandle as ElementHandleImpl
164164
from playwright.file_chooser import FileChooser as FileChooserImpl
165165
from playwright.frame import Frame as FrameImpl
166-
from playwright.helper import ConsoleMessageLocation, Credentials, MousePosition, Error, FilePayload, SelectOption, RequestFailure, Viewport, DeviceDescriptor, IntSize, FloatRect, Geolocation, ProxyServer, PdfMargins, ResourceTiming
166+
from playwright.helper import ConsoleMessageLocation, Credentials, MousePosition, Error, FilePayload, SelectOption, RequestFailure, Viewport, DeviceDescriptor, IntSize, FloatRect, Geolocation, ProxyServer, PdfMargins, ResourceTiming, RecordHarOptions
167167
from playwright.input import Keyboard as KeyboardImpl, Mouse as MouseImpl, Touchscreen as TouchscreenImpl
168168
from playwright.js_handle import JSHandle as JSHandleImpl
169169
from playwright.network import Request as RequestImpl, Response as ResponseImpl, Route as RouteImpl

tests/assets/har.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<title>HAR Page</title>
2+
<link rel='stylesheet' href='./one-style.css'>
3+
<div>hello, world!</div>

tests/async/test_har.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
import base64
16+
import json
17+
import os
18+
19+
20+
async def test_should_work(browser, server, tmpdir):
21+
path = os.path.join(tmpdir, "log.har")
22+
context = await browser.newContext(recordHar={"path": path})
23+
page = await context.newPage()
24+
await page.goto(server.EMPTY_PAGE)
25+
await context.close()
26+
with open(path) as f:
27+
data = json.load(f)
28+
assert "log" in data
29+
30+
31+
async def test_should_omit_content(browser, server, tmpdir):
32+
path = os.path.join(tmpdir, "log.har")
33+
context = await browser.newContext(recordHar={"path": path, "omitContent": True})
34+
page = await context.newPage()
35+
await page.goto(server.PREFIX + "/har.html")
36+
await context.close()
37+
with open(path) as f:
38+
data = json.load(f)
39+
assert "log" in data
40+
log = data["log"]
41+
42+
content1 = log["entries"][0]["response"]["content"]
43+
assert "text" not in content1
44+
45+
46+
async def test_should_include_content(browser, server, tmpdir):
47+
path = os.path.join(tmpdir, "log.har")
48+
context = await browser.newContext(recordHar={"path": path})
49+
page = await context.newPage()
50+
await page.goto(server.PREFIX + "/har.html")
51+
await context.close()
52+
with open(path) as f:
53+
data = json.load(f)
54+
assert "log" in data
55+
log = data["log"]
56+
57+
content1 = log["entries"][0]["response"]["content"]
58+
print(content1)
59+
assert content1["encoding"] == "base64"
60+
assert content1["mimeType"] == "text/html"
61+
s = base64.b64decode(content1["text"]).decode()
62+
assert "HAR Page" in s

tests/async/test_interception.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
1415
import asyncio
1516
import json
1617

tests/async/test_issues.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
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+
115
import pytest
216

317

tests/sync/test_har.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
import base64
16+
import json
17+
import os
18+
19+
20+
def test_should_work(browser, server, tmpdir):
21+
path = os.path.join(tmpdir, "log.har")
22+
context = browser.newContext(recordHar={"path": path})
23+
page = context.newPage()
24+
page.goto(server.EMPTY_PAGE)
25+
context.close()
26+
with open(path) as f:
27+
data = json.load(f)
28+
assert "log" in data
29+
30+
31+
def test_should_omit_content(browser, server, tmpdir):
32+
path = os.path.join(tmpdir, "log.har")
33+
context = browser.newContext(recordHar={"path": path, "omitContent": True})
34+
page = context.newPage()
35+
page.goto(server.PREFIX + "/har.html")
36+
context.close()
37+
with open(path) as f:
38+
data = json.load(f)
39+
assert "log" in data
40+
log = data["log"]
41+
42+
content1 = log["entries"][0]["response"]["content"]
43+
assert "text" not in content1
44+
45+
46+
def test_should_include_content(browser, server, tmpdir):
47+
path = os.path.join(tmpdir, "log.har")
48+
context = browser.newContext(recordHar={"path": path})
49+
page = context.newPage()
50+
page.goto(server.PREFIX + "/har.html")
51+
context.close()
52+
with open(path) as f:
53+
data = json.load(f)
54+
assert "log" in data
55+
log = data["log"]
56+
57+
content1 = log["entries"][0]["response"]["content"]
58+
print(content1)
59+
assert content1["encoding"] == "base64"
60+
assert content1["mimeType"] == "text/html"
61+
s = base64.b64decode(content1["text"]).decode()
62+
assert "HAR Page" in s

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy