Skip to content

Commit 2794a3b

Browse files
authored
api(video): support video accessor from 1.5.1 (#238)
1 parent 6a649d8 commit 2794a3b

12 files changed

+193
-3
lines changed

Diff for: api.json

+1-1
Large diffs are not rendered by default.

Diff for: build_package.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from playwright.path_utils import get_file_dirname
2424

25-
driver_version = "0.150.0"
25+
driver_version = "0.151.0"
2626

2727
if not os.path.exists("driver"):
2828
os.makedirs("driver")

Diff for: playwright/async_api.py

+32
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from playwright.page import Worker as WorkerImpl
6565
from playwright.playwright import Playwright as PlaywrightImpl
6666
from playwright.selectors import Selectors as SelectorsImpl
67+
from playwright.video import Video as VideoImpl
6768

6869
NoneType = type(None)
6970

@@ -3127,6 +3128,25 @@ async def saveAs(self, path: typing.Union[str, pathlib.Path]) -> NoneType:
31273128
mapping.register(DownloadImpl, Download)
31283129

31293130

3131+
class Video(AsyncBase):
3132+
def __init__(self, obj: VideoImpl):
3133+
super().__init__(obj)
3134+
3135+
async def path(self) -> str:
3136+
"""Video.path
3137+
3138+
Returns the file system path this video will be recorded to. The video is guaranteed to be written to the filesystem upon closing the browser context.
3139+
3140+
Returns
3141+
-------
3142+
str
3143+
"""
3144+
return mapping.from_maybe_impl(await self._impl_obj.path())
3145+
3146+
3147+
mapping.register(VideoImpl, Video)
3148+
3149+
31303150
class BindingCall(AsyncBase):
31313151
def __init__(self, obj: BindingCallImpl):
31323152
super().__init__(obj)
@@ -3235,6 +3255,18 @@ def workers(self) -> typing.List["Worker"]:
32353255
"""
32363256
return mapping.from_impl_list(self._impl_obj.workers)
32373257

3258+
@property
3259+
def video(self) -> typing.Union["Video", NoneType]:
3260+
"""Page.video
3261+
3262+
Video object associated with this page.
3263+
3264+
Returns
3265+
-------
3266+
Optional[Video]
3267+
"""
3268+
return mapping.from_impl_nullable(self._impl_obj.video)
3269+
32383270
def remove_listener(self, event: str, f: typing.Any) -> NoneType:
32393271
return mapping.from_maybe_impl(
32403272
self._impl_obj.remove_listener(event=event, f=mapping.to_impl(f))

Diff for: playwright/browser.py

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ async def newContext(
103103
context = from_channel(channel)
104104
self._contexts.append(context)
105105
context._browser = self
106+
context._options = params
106107
return context
107108

108109
async def newPage(

Diff for: playwright/browser_context.py

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def __init__(
5858
self._browser: Optional["Browser"] = None
5959
self._owner_page: Optional[Page] = None
6060
self._is_closed_or_closing = False
61+
self._options: Dict[str, Any] = {}
6162

6263
self._channel.on(
6364
"bindingCall",

Diff for: playwright/browser_type.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,11 @@ async def launchPersistentContext(
115115
params["extraHTTPHeaders"] = serialize_headers(extraHTTPHeaders)
116116
normalize_launch_params(params)
117117
try:
118-
return from_channel(
118+
context = from_channel(
119119
await self._channel.send("launchPersistentContext", params)
120120
)
121+
context._options = params
122+
return context
121123
except Exception as e:
122124
if f"{self.name}-" in str(e):
123125
raise not_installed_error(f'"{self.name}" browser was not found.')

Diff for: playwright/page.py

+18
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
serialize_argument,
6060
)
6161
from playwright.network import Request, Response, Route, serialize_headers
62+
from playwright.video import Video
6263
from playwright.wait_helper import WaitHelper
6364

6465
if sys.version_info >= (3, 8): # pragma: no cover
@@ -115,6 +116,7 @@ def __init__(
115116
self._routes: List[RouteHandlerEntry] = []
116117
self._owned_context: Optional["BrowserContext"] = None
117118
self._timeout_settings: TimeoutSettings = TimeoutSettings(None)
119+
self._video: Optional[Video] = None
118120

119121
self._channel.on(
120122
"bindingCall",
@@ -201,6 +203,12 @@ def __init__(
201203
from_channel(params["route"]), from_channel(params["request"])
202204
),
203205
)
206+
self._channel.on(
207+
"video",
208+
lambda params: cast(Video, self.video)._set_relative_path(
209+
params["relativePath"]
210+
),
211+
)
204212
self._channel.on(
205213
"worker", lambda params: self._on_worker(from_channel(params["worker"]))
206214
)
@@ -775,6 +783,16 @@ async def pdf(
775783
fd.write(decoded_binary)
776784
return decoded_binary
777785

786+
@property
787+
def video(
788+
self,
789+
) -> Optional[Video]:
790+
if not self._browser_context._options.get("videosPath"):
791+
return None
792+
if not self._video:
793+
self._video = Video(self)
794+
return self._video
795+
778796
def expect_event(
779797
self,
780798
event: str,

Diff for: playwright/sync_api.py

+32
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from playwright.playwright import Playwright as PlaywrightImpl
6565
from playwright.selectors import Selectors as SelectorsImpl
6666
from playwright.sync_base import EventContextManager, SyncBase, mapping
67+
from playwright.video import Video as VideoImpl
6768

6869
NoneType = type(None)
6970

@@ -3255,6 +3256,25 @@ def saveAs(self, path: typing.Union[str, pathlib.Path]) -> NoneType:
32553256
mapping.register(DownloadImpl, Download)
32563257

32573258

3259+
class Video(SyncBase):
3260+
def __init__(self, obj: VideoImpl):
3261+
super().__init__(obj)
3262+
3263+
def path(self) -> str:
3264+
"""Video.path
3265+
3266+
Returns the file system path this video will be recorded to. The video is guaranteed to be written to the filesystem upon closing the browser context.
3267+
3268+
Returns
3269+
-------
3270+
str
3271+
"""
3272+
return mapping.from_maybe_impl(self._sync(self._impl_obj.path()))
3273+
3274+
3275+
mapping.register(VideoImpl, Video)
3276+
3277+
32583278
class BindingCall(SyncBase):
32593279
def __init__(self, obj: BindingCallImpl):
32603280
super().__init__(obj)
@@ -3363,6 +3383,18 @@ def workers(self) -> typing.List["Worker"]:
33633383
"""
33643384
return mapping.from_impl_list(self._impl_obj.workers)
33653385

3386+
@property
3387+
def video(self) -> typing.Union["Video", NoneType]:
3388+
"""Page.video
3389+
3390+
Video object associated with this page.
3391+
3392+
Returns
3393+
-------
3394+
Optional[Video]
3395+
"""
3396+
return mapping.from_impl_nullable(self._impl_obj.video)
3397+
33663398
def remove_listener(self, event: str, f: typing.Any) -> NoneType:
33673399
return mapping.from_maybe_impl(
33683400
self._impl_obj.remove_listener(event=event, f=mapping.to_impl(f))

Diff for: playwright/video.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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 os
16+
from typing import TYPE_CHECKING, cast
17+
18+
if TYPE_CHECKING: # pragma: no cover
19+
from playwright.page import Page
20+
21+
22+
class Video:
23+
def __init__(self, page: "Page") -> None:
24+
self._loop = page._loop
25+
self._page = page
26+
self._path_future = page._loop.create_future()
27+
28+
async def path(self) -> str:
29+
return await self._path_future
30+
31+
def _set_relative_path(self, relative_path: str) -> None:
32+
self._path_future.set_result(
33+
os.path.join(
34+
cast(str, self._page._browser_context._options.get("videosPath")),
35+
relative_path,
36+
)
37+
)

Diff for: scripts/generate_api.py

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from playwright.page import BindingCall, Page, Worker
4444
from playwright.playwright import Playwright
4545
from playwright.selectors import Selectors
46+
from playwright.video import Video
4647

4748

4849
def process_type(value: Any, param: bool = False) -> str:
@@ -169,6 +170,7 @@ def return_value(value: Any) -> List[str]:
169170
from playwright.page import BindingCall as BindingCallImpl, Page as PageImpl, Worker as WorkerImpl
170171
from playwright.playwright import Playwright as PlaywrightImpl
171172
from playwright.selectors import Selectors as SelectorsImpl
173+
from playwright.video import Video as VideoImpl
172174
"""
173175

174176
all_types = [
@@ -187,6 +189,7 @@ def return_value(value: Any) -> List[str]:
187189
ConsoleMessage,
188190
Dialog,
189191
Download,
192+
Video,
190193
BindingCall,
191194
Page,
192195
BrowserContext,

Diff for: tests/async/test_video.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 os
16+
17+
18+
async def test_should_expose_video_path(browser, tmpdir, server):
19+
page = await browser.newPage(videosPath=str(tmpdir))
20+
await page.goto(server.PREFIX + "/grid.html")
21+
path = await page.video.path()
22+
assert str(tmpdir) in path
23+
await page.context.close()
24+
25+
26+
async def test_short_video_should_exist(browser, tmpdir, server):
27+
page = await browser.newPage(videosPath=str(tmpdir))
28+
await page.goto(server.PREFIX + "/grid.html")
29+
path = await page.video.path()
30+
assert str(tmpdir) in path
31+
await page.context.close()
32+
assert os.path.exists(path)

Diff for: tests/sync/test_video.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 os
16+
17+
18+
def test_should_expose_video_path(browser, tmpdir, server):
19+
page = browser.newPage(videosPath=str(tmpdir))
20+
page.goto(server.PREFIX + "/grid.html")
21+
path = page.video.path()
22+
assert str(tmpdir) in path
23+
page.context.close()
24+
25+
26+
def test_video_should_exist(browser, tmpdir, server):
27+
page = browser.newPage(videosPath=str(tmpdir))
28+
page.goto(server.PREFIX + "/grid.html")
29+
path = page.video.path()
30+
assert str(tmpdir) in path
31+
page.context.close()
32+
assert os.path.exists(path)

0 commit comments

Comments
 (0)