Skip to content

Commit

Permalink
api(video): support video accessor from 1.5.1 (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Oct 15, 2020
1 parent 6a649d8 commit 2794a3b
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 3 deletions.
2 changes: 1 addition & 1 deletion api.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build_package.py
Expand Up @@ -22,7 +22,7 @@

from playwright.path_utils import get_file_dirname

driver_version = "0.150.0"
driver_version = "0.151.0"

if not os.path.exists("driver"):
os.makedirs("driver")
Expand Down
32 changes: 32 additions & 0 deletions playwright/async_api.py
Expand Up @@ -64,6 +64,7 @@
from playwright.page import Worker as WorkerImpl
from playwright.playwright import Playwright as PlaywrightImpl
from playwright.selectors import Selectors as SelectorsImpl
from playwright.video import Video as VideoImpl

NoneType = type(None)

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


class Video(AsyncBase):
def __init__(self, obj: VideoImpl):
super().__init__(obj)

async def path(self) -> str:
"""Video.path
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.
Returns
-------
str
"""
return mapping.from_maybe_impl(await self._impl_obj.path())


mapping.register(VideoImpl, Video)


class BindingCall(AsyncBase):
def __init__(self, obj: BindingCallImpl):
super().__init__(obj)
Expand Down Expand Up @@ -3235,6 +3255,18 @@ def workers(self) -> typing.List["Worker"]:
"""
return mapping.from_impl_list(self._impl_obj.workers)

@property
def video(self) -> typing.Union["Video", NoneType]:
"""Page.video
Video object associated with this page.
Returns
-------
Optional[Video]
"""
return mapping.from_impl_nullable(self._impl_obj.video)

def remove_listener(self, event: str, f: typing.Any) -> NoneType:
return mapping.from_maybe_impl(
self._impl_obj.remove_listener(event=event, f=mapping.to_impl(f))
Expand Down
1 change: 1 addition & 0 deletions playwright/browser.py
Expand Up @@ -103,6 +103,7 @@ async def newContext(
context = from_channel(channel)
self._contexts.append(context)
context._browser = self
context._options = params
return context

async def newPage(
Expand Down
1 change: 1 addition & 0 deletions playwright/browser_context.py
Expand Up @@ -58,6 +58,7 @@ def __init__(
self._browser: Optional["Browser"] = None
self._owner_page: Optional[Page] = None
self._is_closed_or_closing = False
self._options: Dict[str, Any] = {}

self._channel.on(
"bindingCall",
Expand Down
4 changes: 3 additions & 1 deletion playwright/browser_type.py
Expand Up @@ -115,9 +115,11 @@ async def launchPersistentContext(
params["extraHTTPHeaders"] = serialize_headers(extraHTTPHeaders)
normalize_launch_params(params)
try:
return from_channel(
context = from_channel(
await self._channel.send("launchPersistentContext", params)
)
context._options = params
return context
except Exception as e:
if f"{self.name}-" in str(e):
raise not_installed_error(f'"{self.name}" browser was not found.')
Expand Down
18 changes: 18 additions & 0 deletions playwright/page.py
Expand Up @@ -59,6 +59,7 @@
serialize_argument,
)
from playwright.network import Request, Response, Route, serialize_headers
from playwright.video import Video
from playwright.wait_helper import WaitHelper

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

self._channel.on(
"bindingCall",
Expand Down Expand Up @@ -201,6 +203,12 @@ def __init__(
from_channel(params["route"]), from_channel(params["request"])
),
)
self._channel.on(
"video",
lambda params: cast(Video, self.video)._set_relative_path(
params["relativePath"]
),
)
self._channel.on(
"worker", lambda params: self._on_worker(from_channel(params["worker"]))
)
Expand Down Expand Up @@ -775,6 +783,16 @@ async def pdf(
fd.write(decoded_binary)
return decoded_binary

@property
def video(
self,
) -> Optional[Video]:
if not self._browser_context._options.get("videosPath"):
return None
if not self._video:
self._video = Video(self)
return self._video

def expect_event(
self,
event: str,
Expand Down
32 changes: 32 additions & 0 deletions playwright/sync_api.py
Expand Up @@ -64,6 +64,7 @@
from playwright.playwright import Playwright as PlaywrightImpl
from playwright.selectors import Selectors as SelectorsImpl
from playwright.sync_base import EventContextManager, SyncBase, mapping
from playwright.video import Video as VideoImpl

NoneType = type(None)

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


class Video(SyncBase):
def __init__(self, obj: VideoImpl):
super().__init__(obj)

def path(self) -> str:
"""Video.path
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.
Returns
-------
str
"""
return mapping.from_maybe_impl(self._sync(self._impl_obj.path()))


mapping.register(VideoImpl, Video)


class BindingCall(SyncBase):
def __init__(self, obj: BindingCallImpl):
super().__init__(obj)
Expand Down Expand Up @@ -3363,6 +3383,18 @@ def workers(self) -> typing.List["Worker"]:
"""
return mapping.from_impl_list(self._impl_obj.workers)

@property
def video(self) -> typing.Union["Video", NoneType]:
"""Page.video
Video object associated with this page.
Returns
-------
Optional[Video]
"""
return mapping.from_impl_nullable(self._impl_obj.video)

def remove_listener(self, event: str, f: typing.Any) -> NoneType:
return mapping.from_maybe_impl(
self._impl_obj.remove_listener(event=event, f=mapping.to_impl(f))
Expand Down
37 changes: 37 additions & 0 deletions playwright/video.py
@@ -0,0 +1,37 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from typing import TYPE_CHECKING, cast

if TYPE_CHECKING: # pragma: no cover
from playwright.page import Page


class Video:
def __init__(self, page: "Page") -> None:
self._loop = page._loop
self._page = page
self._path_future = page._loop.create_future()

async def path(self) -> str:
return await self._path_future

def _set_relative_path(self, relative_path: str) -> None:
self._path_future.set_result(
os.path.join(
cast(str, self._page._browser_context._options.get("videosPath")),
relative_path,
)
)
3 changes: 3 additions & 0 deletions scripts/generate_api.py
Expand Up @@ -43,6 +43,7 @@
from playwright.page import BindingCall, Page, Worker
from playwright.playwright import Playwright
from playwright.selectors import Selectors
from playwright.video import Video


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

all_types = [
Expand All @@ -187,6 +189,7 @@ def return_value(value: Any) -> List[str]:
ConsoleMessage,
Dialog,
Download,
Video,
BindingCall,
Page,
BrowserContext,
Expand Down
32 changes: 32 additions & 0 deletions tests/async/test_video.py
@@ -0,0 +1,32 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os


async def test_should_expose_video_path(browser, tmpdir, server):
page = await browser.newPage(videosPath=str(tmpdir))
await page.goto(server.PREFIX + "/grid.html")
path = await page.video.path()
assert str(tmpdir) in path
await page.context.close()


async def test_short_video_should_exist(browser, tmpdir, server):
page = await browser.newPage(videosPath=str(tmpdir))
await page.goto(server.PREFIX + "/grid.html")
path = await page.video.path()
assert str(tmpdir) in path
await page.context.close()
assert os.path.exists(path)
32 changes: 32 additions & 0 deletions tests/sync/test_video.py
@@ -0,0 +1,32 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os


def test_should_expose_video_path(browser, tmpdir, server):
page = browser.newPage(videosPath=str(tmpdir))
page.goto(server.PREFIX + "/grid.html")
path = page.video.path()
assert str(tmpdir) in path
page.context.close()


def test_video_should_exist(browser, tmpdir, server):
page = browser.newPage(videosPath=str(tmpdir))
page.goto(server.PREFIX + "/grid.html")
path = page.video.path()
assert str(tmpdir) in path
page.context.close()
assert os.path.exists(path)

0 comments on commit 2794a3b

Please sign in to comment.