""" Helper utilities. """ import sys import os import urllib.request import json 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 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" ) 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 ) 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'(.*?)', 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 element.""" if elem is None: return None assert elem.tag_name == "img" 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 )