Source code

Revision control

Copy as Markdown

Other Tools

from typing import Any, Callable, Dict, List, Mapping
from webdriver.bidi.modules.script import ContextTarget
# Compares 2 objects recursively.
# Actual value can have more keys as part of the forwards-compat design.
# Expected value can be a callable delegate, asserting the value.
def recursive_compare(expected: Any, actual: Any) -> None:
if callable(expected):
expected(actual)
return
if isinstance(actual, List) and isinstance(expected, List):
assert len(expected) == len(actual)
for index, _ in enumerate(expected):
recursive_compare(expected[index], actual[index])
return
if isinstance(actual, Dict) and isinstance(expected, Dict):
# Actual Mapping 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())}"
for key in expected.keys():
recursive_compare(expected[key], actual[key])
return
assert expected == actual
def any_bool(actual: Any) -> None:
assert isinstance(actual, bool)
def any_dict(actual: Any) -> None:
assert isinstance(actual, dict)
def any_int(actual: Any) -> None:
assert isinstance(actual, int)
def any_int_or_null(actual: Any) -> None:
if actual is not None:
any_int(actual)
def any_list(actual: Any) -> None:
assert isinstance(actual, list)
def any_list_or_null(actual: Any) -> None:
if actual is not None:
any_list(actual)
def any_string(actual: Any) -> None:
assert isinstance(actual, str)
def any_string_or_null(actual: Any) -> None:
if actual is not None:
any_string(actual)
def int_interval(start: int, end: int) -> Callable[[Any], None]:
def _(actual: Any) -> None:
any_int(actual)
assert start <= actual <= end
return _
def assert_cookies(cookies, expected_cookies):
assert len(cookies) == len(expected_cookies)
expected = sorted(expected_cookies, key=lambda cookie: cookie["name"])
actual = sorted(cookies, key=lambda cookie: cookie["name"])
recursive_compare(expected, actual)
def assert_handle(obj: Mapping[str, Any], should_contain_handle: bool) -> None:
if should_contain_handle:
assert "handle" in obj, f"Result should contain `handle`. Actual: {obj}"
assert isinstance(obj["handle"], str), f"`handle` should be a string, but was {type(obj['handle'])}"
# Recursively check that handle is not found in any of the nested values.
if "value" in obj:
value = obj["value"]
if type(value) is list:
for v in value:
if type(v) is dict:
assert_handle(v, False)
if type(value) is dict:
for v in value.values():
if type(v) is dict:
assert_handle(v, False)
else:
assert "handle" not in obj, f"Result should not contain `handle`. Actual: {obj}"
async def create_console_api_message(bidi_session, context: str, text: str):
await bidi_session.script.call_function(
function_declaration="""(text) => console.log(text)""",
arguments=[{"type": "string", "value": text}],
await_promise=False,
target=ContextTarget(context["context"]),
)
return text
async def get_device_pixel_ratio(bidi_session, context: str) -> float:
result = await bidi_session.script.call_function(
function_declaration="""() => {
return window.devicePixelRatio;
}""",
target=ContextTarget(context["context"]),
await_promise=False,
)
return result["value"]
async def get_element_dimensions(bidi_session, context, element):
result = await bidi_session.script.call_function(
arguments=[element],
function_declaration="""(element) => {
const rect = element.getBoundingClientRect();
return { height: rect.height, width: rect.width }
}""",
target=ContextTarget(context["context"]),
await_promise=False,
)
return remote_mapping_to_dict(result["value"])
async def get_viewport_dimensions(bidi_session, context: str, with_scrollbar: bool = True):
if with_scrollbar == True:
expression = """
({
height: window.innerHeight,
width: window.innerWidth,
});
"""
else:
expression = """
({
height: document.documentElement.clientHeight,
width: document.documentElement.clientWidth,
});
"""
result = await bidi_session.script.evaluate(
expression=expression,
target=ContextTarget(context["context"]),
await_promise=False,
)
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:
obj[key] = value["value"]
return obj