import collections
import math
import sys
from urllib.parse import urlparse
import webdriver
from tests.support import defaults
from tests.support.sync import Poll
def ignore_exceptions(f):
def inner(*args, **kwargs):
try:
return f(*args, **kwargs)
except webdriver.error.WebDriverException as e:
print("Ignored exception %s" % e, file=sys.stderr)
inner.__name__ = f.__name__
return inner
def cleanup_session(session):
"""Clean-up the current session for a clean state."""
@ignore_exceptions
def _dismiss_user_prompts(session):
"""Dismiss any open user prompts in windows."""
current_window = session.window_handle
for window in _windows(session):
session.window_handle = window
try:
session.alert.dismiss()
except webdriver.NoSuchAlertException:
pass
session.window_handle = current_window
@ignore_exceptions
def _ensure_valid_window(session):
"""If current window was closed, ensure to have a valid one selected."""
try:
session.window_handle
except webdriver.NoSuchWindowException:
handles = session.handles
if handles:
# Update only when there is at least one valid window left.
session.window_handle = handles[0]
@ignore_exceptions
def _restore_timeouts(session):
"""Restore modified timeouts to their default values."""
session.timeouts.implicit = defaults.IMPLICIT_WAIT_TIMEOUT
session.timeouts.page_load = defaults.PAGE_LOAD_TIMEOUT
session.timeouts.script = defaults.SCRIPT_TIMEOUT
@ignore_exceptions
def _restore_window_state(session):
"""Reset window to an acceptable size.
This also includes bringing it out of maximized, minimized,
or fullscreen state.
"""
if session.capabilities.get("setWindowRect"):
session.window.size = defaults.WINDOW_SIZE
@ignore_exceptions
def _restore_windows(session):
"""Close superfluous windows opened by the test.
It will not end the session implicitly by closing the last window.
"""
current_window = session.window_handle
for window in _windows(session, exclude=[current_window]):
session.window_handle = window
if len(session.handles) > 1:
session.window.close()
session.window_handle = current_window
_restore_timeouts(session)
_ensure_valid_window(session)
_dismiss_user_prompts(session)
_restore_windows(session)
_restore_window_state(session)
_switch_to_top_level_browsing_context(session)
@ignore_exceptions
def _switch_to_top_level_browsing_context(session):
"""If the current browsing context selected by WebDriver is a
`<frame>` or an `<iframe>`, switch it back to the top-level
browsing context.
"""
session.switch_frame(None)
def _windows(session, exclude=None):
"""Set of window handles, filtered by an `exclude` list if
provided.
"""
if exclude is None:
exclude = []
wins = [w for w in session.handles if w not in exclude]
return set(wins)
def clear_all_cookies(session):
"""Removes all cookies associated with the current active document"""
session.transport.send("DELETE", "session/%s/cookie" % session.session_id)
def deep_update(source, overrides):
"""
Update a nested dictionary or similar mapping.
Modify ``source`` in place.
"""
for key, value in overrides.items():
if isinstance(value, collections.abc.Mapping) and value:
returned = deep_update(source.get(key, {}), value)
source[key] = returned
else:
source[key] = overrides[key]
return source
def document_dimensions(session):
return tuple(session.execute_script("""
const {devicePixelRatio} = window;
const {width, height} = document.documentElement.getBoundingClientRect();
return [width * devicePixelRatio, height * devicePixelRatio];
"""))
def center_point(element):
"""Calculates the in-view center point of a web element."""
inner_width, inner_height = element.session.execute_script(
"return [window.innerWidth, window.innerHeight]")
rect = element.rect
# calculate the intersection of the rect that is inside the viewport
visible = {
"left": max(0, min(rect["x"], rect["x"] + rect["width"])),
"right": min(inner_width, max(rect["x"], rect["x"] + rect["width"])),
"top": max(0, min(rect["y"], rect["y"] + rect["height"])),
"bottom": min(inner_height, max(rect["y"], rect["y"] + rect["height"])),
}
# arrive at the centre point of the visible rectangle
x = (visible["left"] + visible["right"]) / 2.0
y = (visible["top"] + visible["bottom"]) / 2.0
# convert to CSS pixels, as centre point can be float
return (math.floor(x), math.floor(y))
def document_hidden(session):
return session.execute_script("return document.hidden")
def document_location(session):
"""
Unlike ``webdriver.Session#url``, which always returns
the top-level browsing context's URL, this returns
the current browsing context's active document's URL.
"""
return session.execute_script("return document.location.href")
def element_rect(session, element):
return session.execute_script("""
let element = arguments[0];
let rect = element.getBoundingClientRect();
return {
x: rect.left + window.pageXOffset,
y: rect.top + window.pageYOffset,
width: rect.width,
height: rect.height,
};
""", args=(element,))
def is_element_in_viewport(session, element):
"""Check if element is outside of the viewport"""
return session.execute_script("""
let el = arguments[0];
let rect = el.getBoundingClientRect();
let viewport = {
height: window.innerHeight || document.documentElement.clientHeight,
width: window.innerWidth || document.documentElement.clientWidth,
};
return !(rect.right < 0 || rect.bottom < 0 ||
rect.left > viewport.width || rect.top > viewport.height)
""", args=(element,))
def is_fullscreen(session):
# At the time of writing, WebKit does not conform to the
# Fullscreen API specification.
#
# Remove the prefixed fallback when
# https://bugs.webkit.org/show_bug.cgi?id=158125 is fixed.
return session.execute_script("""
return !!(window.fullScreen || document.webkitIsFullScreen)
""")
def is_maximized(session):
dimensions = session.execute_script("""
return {
availWidth: screen.availWidth,
availHeight: screen.availHeight,
windowWidth: window.outerWidth,
windowHeight: window.outerHeight,
}
""")
return (
# The maximized window can still have a border attached which would
# cause its dimensions to exceed the whole available screen.
dimensions["windowWidth"] >= dimensions["availWidth"] and
dimensions["windowHeight"] >= dimensions["availHeight"] and
# Only return true if the window is not in fullscreen mode
not is_fullscreen(session)
)
def filter_dict(source, d):
"""Filter `source` dict to only contain same keys as `d` dict.
:param source: dictionary to filter.
:param d: dictionary whose keys determine the filtering.
"""
return {k: source[k] for k in d.keys()}
def filter_supported_key_events(all_events, expected):
events = [filter_dict(e, expected[0]) for e in all_events]
if len(events) > 0 and events[0]["code"] is None:
# Remove 'code' entry if browser doesn't support it
expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
events = [filter_dict(e, expected[0]) for e in events]
return (events, expected)
def get_origin_from_url(url):
parsed_uri = urlparse(url)
return '{uri.scheme}://{uri.netloc}'.format(uri=parsed_uri)
def wait_for_new_handle(session, handles_before):
def find_new_handle(session):
new_handles = list(set(session.handles) - set(handles_before))
if new_handles and len(new_handles) == 1:
return new_handles[0]
return None
wait = Poll(
session,
timeout=5,
message="No new window has been opened")
return wait.until(find_new_handle)