A search engine for MMP's eASLRB.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
asl-rulebook2/asl_rulebook2/webapp/tests/utils.py

286 lines
9.9 KiB

""" Helper utilities. """
import sys
import os
import urllib.request
import json
3 years ago
import re
import uuid
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException, TimeoutException
3 years ago
from asl_rulebook2.utils import strip_html
from asl_rulebook2.webapp import tests as webapp_tests
_webapp = None
_webdriver = None
# ---------------------------------------------------------------------
def init_webapp( webapp, webdriver, **options ):
"""Initialize the webapp."""
# initialize
expected_warnings = options.pop( "expected_warnings", [] )
expected_errors = options.pop( "expected_errors", [] )
# initialize
global _webapp, _webdriver
_webapp = webapp
_webdriver = webdriver
options = {
key.replace("_","-"): val
for key, val in options.items()
}
# load the webapp
webapp.control_tests.set_app_config_val( "BLOCKING_STARTUP_TASKS", True )
webdriver.get( make_webapp_main_url( options ) )
_wait_for_webapp()
# make sure there were no errors or warnings
url = webapp.url_for( "get_startup_msgs" )
with urllib.request.urlopen( url ) as resp:
startup_msgs = json.load( resp )
errors = startup_msgs.pop( "error", [] )
errors = [ e[0] for e in errors ]
assert set( errors ) == set( expected_errors )
warnings = startup_msgs.pop( "warning", [] )
warnings = [ w[0] for w in warnings ]
assert set( warnings ) == set( expected_warnings )
assert not startup_msgs
# reset the user settings
webdriver.delete_all_cookies()
def make_webapp_main_url( options ):
"""Generate the webapp URL."""
if get_pytest_option("webdriver") == "chrome" and get_pytest_option("headless"):
# FUDGE! Headless Chrome doesn't want to show the PDF in the browser,
# it downloads the file and saves it in the current directory :wtf:
options["no-content"] = 1
options["store-msgs"] = 1 # nb: so that we can retrive notification messages
options["no-animations"] = 1
options["reload"] = 1 # nb: force the webapp to reload
return _webapp.url_for( "main", **options )
def refresh_webapp( webdriver ):
"""Refresh the webapp."""
webdriver.refresh()
_wait_for_webapp()
def _wait_for_webapp():
"""Wait for the webapp to finish initialization."""
timeout = 5
wait_for( timeout,
lambda: find_child("#_mainapp-loaded_") \
or ( webapp_tests.pytest_options.enable_prepare and find_child("#_prepareapp-loaded_") )
)
# ---------------------------------------------------------------------
def select_tabbed_page( tabbed_pages_id, tab_id ):
"""Select a tabbed page."""
tabbed_pages = find_child( "#tabbed-pages-" + tabbed_pages_id )
assert tabbed_pages
btn = find_child( ".tab-strip .tab[data-tabid='{}']".format( tab_id ), tabbed_pages )
btn.click()
def find_tabbed_page():
elem = find_child( ".tabbed-page[data-tabid='{}']".format( tab_id ), tabbed_pages )
return elem and elem.is_displayed()
wait_for( 2, find_tabbed_page )
# ---------------------------------------------------------------------
def get_nav_panels():
"""Get the available nav panels."""
return _get_tab_ids( "#nav .tab-strip" )
def get_content_docs():
"""Get the available content docs."""
return _get_tab_ids( "#content .tab-strip" )
def _get_tab_ids( sel ):
"""Get the tabs in a tab-strip."""
tabs = find_children( "{} .tab".format( sel ) )
return [ tab.get_attribute( "data-tabid" ) for tab in tabs ]
def get_curr_target():
"""Get the currently-shown target."""
# check the active tab
elem = find_child( "#content .tab-strip .tab.active" )
if not elem:
return ( None, None )
tab_id = elem.get_attribute( "data-tabid" )
# check the current ruleid
elem = find_child( "#content .tabbed-page[data-tabid='{}'] .content-doc".format( tab_id ) )
ruleid = elem.get_attribute( "data-ruleid" )
if ruleid:
return ( tab_id, ruleid )
page_no = elem.get_attribute( "data-pageno" )
if page_no:
return ( tab_id, int(page_no) )
return tab_id
def check_sr_filters( expected ):
"""Check the search result filter checkboxes."""
sr_filters = find_child( "#search-box .sr-filters" )
if expected:
elems = [
c.get_attribute("name") for c in find_children( "input[type='checkbox']", sr_filters )
if c.is_displayed()
]
assert all( e.startswith("show-") and e.endswith("-sr") for e in elems )
elems = [ e[5:-3] for e in elems ]
assert elems == expected
else:
assert not sr_filters.is_displayed()
# ---------------------------------------------------------------------
#pylint: disable=multiple-statements,missing-function-docstring
def get_last_info(): return get_stored_msg( "info" )
def get_last_warning_msg(): return get_stored_msg( "warning" )
def get_last_error_msg(): return get_stored_msg( "error" )
3 years ago
def get_last_footnote_msg(): return get_stored_msg( "footnote" )
#pylint: enable=multiple-statements,missing-function-docstring
def get_stored_msg( msg_type ):
"""Get a message stored for us by the front-end."""
elem = find_child( "#_last-{}-msg_".format(msg_type), _webdriver )
assert elem.tag_name == "textarea"
return elem.get_attribute( "value" )
def set_stored_msg( msg_type, val ):
"""Set a message for the front-end."""
elem = find_child( "#_last-{}-msg_".format(msg_type), _webdriver )
assert elem.tag_name == "textarea"
_webdriver.execute_script( "arguments[0].value = arguments[1]", elem, val )
def set_stored_msg_marker( msg_type ):
"""Store marker text in the message buffer (so we can tell if the front-end changes it)."""
marker = "marker:{}:{}".format( msg_type, uuid.uuid4() )
set_stored_msg( msg_type, marker )
return marker
# ---------------------------------------------------------------------
def find_child( sel, parent=None ):
"""Find a single child element."""
try:
if parent is None:
parent = _webdriver
return parent.find_element( By.CSS_SELECTOR, sel )
except NoSuchElementException:
return None
def find_children( sel, parent=None ):
"""Find child elements."""
try:
if parent is None:
parent = _webdriver
return parent.find_elements( By.CSS_SELECTOR, sel )
except NoSuchElementException:
return None
def get_classes( elem ):
"""Get the element's classes."""
classes = elem.get_attribute( "class" )
return classes.split()
def has_class( elem, class_name ):
"""Check if an element has a specified CSS class."""
return class_name in get_classes( elem )
3 years ago
def unload_elem( save_loc, key, elem, adjust_hilites=False ):
"""Unload a single element."""
if not elem:
return False
if elem.tag_name in ("div", "span"):
val = unload_sr_text( elem ) if adjust_hilites else elem.text
elif elem.tag_name == "img":
val = get_image_filename( elem )
else:
assert False, "Unknown element type: " + elem.tag_name
return False
if not val:
return False
save_loc[ key ] = val
return True
def unload_sr_text( elem ):
"""Unload a text value that is part of a search result."""
val = elem.get_attribute( "innerHTML" )
# change how highlighted content is represented
matches = list( re.finditer( r'<span class="hilite">(.*?)</span>', val ) )
for mo in reversed(matches):
val = val[:mo.start()] + "((" + mo.group(1) + "))" + val[mo.end():]
# remove HTML tags
val = strip_html( val ).strip()
return val
def get_image_filename( elem, full=False ):
"""Get the filename of an <img> element."""
if elem is None:
return None
assert elem.tag_name == "img"
3 years ago
src = elem.get_attribute( "src" )
if full:
src = re.sub( r"^http://[^/]+", "", src )
else:
src = os.path.basename( src )
return re.sub( r"/+", "/", src )
# ---------------------------------------------------------------------
def wait_for_elem( timeout, sel, parent=None ):
"""Wait for an element to appear."""
elem = None
def check_elem():
nonlocal elem
elem = find_child( sel, parent )
return elem is not None and elem.is_displayed()
wait_for( timeout, check_elem )
return elem
def wait_for( timeout, func ):
"""Wait for a condition to become true."""
last_val = None
def wrapper( driver ): #pylint: disable=unused-argument
nonlocal last_val
last_val = func()
return last_val
WebDriverWait( _webdriver, timeout, poll_frequency=0.1 ).until( wrapper )
return last_val
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#pylint: disable=missing-function-docstring
def wait_for_info_msg( timeout, expected, contains=True ):
return _do_wait_for_msg( timeout, "info", expected, contains )
def wait_for_warning_msg( timeout, expected, contains=True ):
return _do_wait_for_msg( timeout, "warning", expected, contains )
def wait_for_error_msg( timeout, expected, contains=True ):
return _do_wait_for_msg( timeout, "error", expected, contains )
#pylint: enable=missing-function-docstring
def _do_wait_for_msg( timeout, msg_type, expected, contains ):
"""Wait for a message to be issued."""
func = getattr( sys.modules[__name__], "get_last_{}_msg".format( msg_type ) )
try:
wait_for( timeout,
lambda: expected in func() if contains else expected == func()
)
except TimeoutException:
print( "ERROR: Didn't get expected {} message: {}".format( msg_type, expected ) )
print( "- last {} message: {}".format( msg_type, func() ) )
assert False
# ---------------------------------------------------------------------
def get_pytest_option( opt ):
"""Get a pytest configuration option."""
return getattr( webapp_tests.pytest_options, opt )