Skip to content

Commit

Permalink
[WebDriver BiDi] Add screenshot origin/format tests (#42804)
Browse files Browse the repository at this point in the history
* [WebDriver BiDi] Add screenshot origin/format tests

* Update webdriver/tests/bidi/browsing_context/capture_screenshot/origin.py

Co-authored-by: Henrik Skupin <[email protected]>

* Update webdriver/tests/bidi/browsing_context/capture_screenshot/__init__.py

Co-authored-by: Henrik Skupin <[email protected]>

* fix lint

---------

Co-authored-by: Alex Rudenko <[email protected]>
Co-authored-by: Henrik Skupin <[email protected]>
Co-authored-by: Alex Rudenko <[email protected]>
  • Loading branch information
4 people authored Nov 24, 2023
1 parent ca74d58 commit a8bb3e3
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 60 deletions.
16 changes: 12 additions & 4 deletions tools/webdriver/webdriver/bidi/modules/browsing_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@


class ElementOptions(Dict[str, Any]):
def __init__(
self, element: Mapping[str, Any]
):
def __init__(self, element: Mapping[str, Any]):
self["type"] = "element"
self["element"] = element

Expand All @@ -31,6 +29,14 @@ class OriginOptions(Enum):
VIEWPORT = "viewport"


class FormatOptions(Dict[str, Any]):
def __init__(self, type: str, quality: Optional[float] = None):
dict.__init__(self, type=type)

if quality is not None:
self["quality"] = quality


class BrowsingContext(BidiModule):
@command
def activate(self, context: str) -> Mapping[str, Any]:
Expand All @@ -42,12 +48,14 @@ def capture_screenshot(
context: str,
clip: Optional[ClipOptions] = None,
origin: Optional[OriginOptions] = None,
format: Optional[FormatOptions] = None,
) -> Mapping[str, Any]:
params: MutableMapping[str, Any] = {"context": context}

if format is not None:
params["format"] = format
if clip is not None:
params["clip"] = clip

if origin is not None:
params["origin"] = origin

Expand Down
24 changes: 21 additions & 3 deletions webdriver/tests/bidi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ def recursive_compare(expected: Any, actual: Any) -> None:

if type(expected) is dict:
# Actual dict can have more keys as part of the forwards-compat design.
assert expected.keys() <= actual.keys(), \
f"Key set should be present: {set(expected.keys()) - set(actual.keys())}"
assert (
expected.keys() <= actual.keys()
), f"Key set should be present: {set(expected.keys()) - set(actual.keys())}"
for key in expected.keys():
recursive_compare(expected[key], actual[key])
return
Expand Down Expand Up @@ -88,7 +89,8 @@ async def get_device_pixel_ratio(bidi_session, context: str) -> float:
return window.devicePixelRatio;
}""",
target=ContextTarget(context["context"]),
await_promise=False)
await_promise=False,
)
return result["value"]


Expand Down Expand Up @@ -122,6 +124,22 @@ async def get_viewport_dimensions(bidi_session, context: str):
return remote_mapping_to_dict(result["value"])


async def get_document_dimensions(bidi_session, context: str):
expression = """
({
height: document.documentElement.scrollHeight,
width: document.documentElement.scrollWidth,
});
"""
result = await bidi_session.script.evaluate(
expression=expression,
target=ContextTarget(context["context"]),
await_promise=False,
)

return remote_mapping_to_dict(result["value"])


def remote_mapping_to_dict(js_object) -> Dict:
obj = {}
for key, value in js_object:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from math import floor
from ... import (
get_device_pixel_ratio,
get_document_dimensions,
get_element_dimensions,
get_viewport_dimensions,
remote_mapping_to_dict,
Expand Down Expand Up @@ -72,6 +73,18 @@ async def get_physical_viewport_dimensions(bidi_session, context):
return (floor(viewport["width"] * dpr), floor(viewport["height"] * dpr))


async def get_physical_document_dimensions(bidi_session, context):
"""Get the physical dimensions of the context's document.
:param bidi_session: BiDiSession
:param context: Browsing context ID
:returns: Tuple of (int, int) containing document width, document height.
"""
document = await get_document_dimensions(bidi_session, context)
dpr = await get_device_pixel_ratio(bidi_session, context)
return (floor(document["width"] * dpr), floor(document["height"] * dpr))


async def get_reference_screenshot(bidi_session, inline, context, html):
"""Get the reference screenshot for the given context and html.
Expand Down
55 changes: 49 additions & 6 deletions webdriver/tests/bidi/browsing_context/capture_screenshot/clip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import pytest
from tests.support.image import png_dimensions

import webdriver.bidi.error as error
from webdriver.bidi.modules.browsing_context import ElementOptions, BoxOptions
from webdriver.bidi.modules.script import ContextTarget

from tests.support.image import png_dimensions


from . import (
get_element_coordinates,
get_physical_element_dimensions,
Expand Down Expand Up @@ -65,7 +69,7 @@ async def test_clip_element(bidi_session, top_context, inline, compare_png_bidi)
assert comparison.equal()


async def test_clip_viewport(bidi_session, top_context, inline, compare_png_bidi):
async def test_clip_box(bidi_session, top_context, inline, compare_png_bidi):
url = inline("<input>")
await bidi_session.browsing_context.navigate(
context=top_context["context"], url=url, wait="complete"
Expand Down Expand Up @@ -150,9 +154,7 @@ async def test_clip_viewport(bidi_session, top_context, inline, compare_png_bidi
assert comparison.equal()


async def test_clip_viewport_scroll_to(
bidi_session, top_context, inline, compare_png_bidi
):
async def test_clip_box_scroll_to(bidi_session, top_context, inline, compare_png_bidi):
element_styles = "background-color: black; width: 50px; height:50px;"

# Render an element inside of viewport for the reference.
Expand Down Expand Up @@ -208,7 +210,7 @@ async def test_clip_viewport_scroll_to(
assert comparison.equal()


async def test_clip_viewport_partially_visible(
async def test_clip_box_partially_visible(
bidi_session, top_context, inline, compare_png_bidi
):
viewport_dimensions = await get_viewport_dimensions(bidi_session, top_context)
Expand Down Expand Up @@ -259,3 +261,44 @@ async def test_clip_viewport_partially_visible(

comparison = await compare_png_bidi(reference_data, new_data)
assert comparison.equal()


async def test_clip_box_outside_of_window_viewport(bidi_session, top_context):
viewport_dimensions = await get_viewport_dimensions(bidi_session, top_context)

with pytest.raises(error.UnableToCaptureScreenException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=BoxOptions(
x=viewport_dimensions["width"],
y=viewport_dimensions["height"],
width=1,
height=1,
),
)


async def test_clip_element_outside_of_window_viewport(
bidi_session, top_context, inline
):
viewport_dimensions = await get_viewport_dimensions(bidi_session, top_context)

element_styles = "background-color: black; width: 50px; height:50px;"
# Render element outside of viewport.
url = inline(
f"""<div style="{element_styles} margin-top: {viewport_dimensions["height"]}px"></div>"""
)
await bidi_session.browsing_context.navigate(
context=top_context["context"], url=url, wait="complete"
)
element = await bidi_session.script.evaluate(
await_promise=False,
expression="document.querySelector('div')",
target=ContextTarget(top_context["context"]),
)

with pytest.raises(error.UnableToCaptureScreenException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=ElementOptions(element=element),
)
39 changes: 39 additions & 0 deletions webdriver/tests/bidi/browsing_context/capture_screenshot/format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pytest

from webdriver.bidi.modules.browsing_context import FormatOptions


@pytest.mark.asyncio
async def test_format_type(bidi_session, top_context, inline):
await bidi_session.browsing_context.navigate(
context=top_context["context"],
url=inline("<div style='margin-top:2000px'>foo</div>"),
wait="complete")

png_screenshot = await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
format=FormatOptions(type="image/png"))
jpeg_screenshot = await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
format=FormatOptions(type="image/jpeg"))

assert png_screenshot != jpeg_screenshot


@pytest.mark.asyncio
async def test_format_quality(bidi_session, top_context, inline):
await bidi_session.browsing_context.navigate(
context=top_context["context"],
url=inline("<div style='margin-top:2000px'>foo</div>"),
wait="complete")

jpeg_quality_screenshot = await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
format=FormatOptions(type="image/jpeg",quality=0.1))
jpeg_high_quality_screenshot = await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
format=FormatOptions(type="image/jpeg",quality=1))

assert jpeg_quality_screenshot != jpeg_high_quality_screenshot

assert len(jpeg_high_quality_screenshot) > len(jpeg_quality_screenshot)
63 changes: 16 additions & 47 deletions webdriver/tests/bidi/browsing_context/capture_screenshot/invalid.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import pytest

from ... import get_viewport_dimensions

import webdriver.bidi.error as error
from webdriver.bidi.modules.browsing_context import ElementOptions, BoxOptions
from webdriver.bidi.modules.browsing_context import (
BoxOptions,
ElementOptions,
FormatOptions,
)
from webdriver.bidi.modules.script import ContextTarget

pytestmark = pytest.mark.asyncio
Expand Down Expand Up @@ -83,7 +85,7 @@ async def test_params_clip_element_sharedId_invalid_value(bidi_session, top_cont


@pytest.mark.parametrize("value", [None, False, "foo", {}, []])
async def test_params_clip_viewport_x_invalid_type(bidi_session, top_context, value):
async def test_params_clip_box_x_invalid_type(bidi_session, top_context, value):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
Expand All @@ -92,7 +94,7 @@ async def test_params_clip_viewport_x_invalid_type(bidi_session, top_context, va


@pytest.mark.parametrize("value", [None, False, "foo", {}, []])
async def test_params_clip_viewport_y_invalid_type(bidi_session, top_context, value):
async def test_params_clip_box_y_invalid_type(bidi_session, top_context, value):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
Expand All @@ -101,9 +103,7 @@ async def test_params_clip_viewport_y_invalid_type(bidi_session, top_context, va


@pytest.mark.parametrize("value", [None, False, "foo", {}, []])
async def test_params_clip_viewport_width_invalid_type(
bidi_session, top_context, value
):
async def test_params_clip_box_width_invalid_type(bidi_session, top_context, value):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
Expand All @@ -112,62 +112,31 @@ async def test_params_clip_viewport_width_invalid_type(


@pytest.mark.parametrize("value", [None, False, "foo", {}, []])
async def test_params_clip_viewport_height_invalid_type(
bidi_session, top_context, value
):
async def test_params_clip_box_height_invalid_type(bidi_session, top_context, value):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=BoxOptions(x=0, y=0, width=0, height=value),
)


async def test_params_clip_viewport_dimensions_invalid_value(bidi_session, top_context):
async def test_params_clip_box_dimensions_invalid_value(bidi_session, top_context):
with pytest.raises(error.UnableToCaptureScreenException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=BoxOptions(x=0, y=0, width=0, height=0),
)


async def test_params_clip_viewport_outside_of_window_viewport(
bidi_session, top_context
):
viewport_dimensions = await get_viewport_dimensions(bidi_session, top_context)

with pytest.raises(error.UnableToCaptureScreenException):
async def test_params_origin_invalid_value(bidi_session, top_context):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=BoxOptions(
x=viewport_dimensions["width"],
y=viewport_dimensions["height"],
width=1,
height=1,
),
context=top_context["context"], origin="page"
)


async def test_params_clip_element_outside_of_window_viewport(
bidi_session, top_context, inline
):
viewport_dimensions = await get_viewport_dimensions(bidi_session, top_context)

element_styles = "background-color: black; width: 50px; height:50px;"
# Render element outside of viewport.
url = inline(
f"""<div style="{element_styles} margin-top: {viewport_dimensions["height"]}px"></div>"""
)
await bidi_session.browsing_context.navigate(
context=top_context["context"], url=url, wait="complete"
)
element = await bidi_session.script.evaluate(
await_promise=False,
expression="document.querySelector('div')",
target=ContextTarget(top_context["context"]),
)

with pytest.raises(error.UnableToCaptureScreenException):
async def test_params_format_invalid_value(bidi_session, top_context):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.browsing_context.capture_screenshot(
context=top_context["context"],
clip=ElementOptions(element=element),
context=top_context["context"], format=FormatOptions(type="image/invalid")
)
Loading

0 comments on commit a8bb3e3

Please sign in to comment.