Compare commits

...

10 Commits

6 changed files with 615 additions and 41 deletions

@ -29,6 +29,7 @@ setup(
# Third-party dependencies; will be automatically installed # Third-party dependencies; will be automatically installed
install_requires = ( install_requires = (
'dill',
'selenium', 'selenium',
), ),

@ -1,3 +1,4 @@
from .api import * from .api import *
from .page import * from .page import *
from .fields import *

@ -0,0 +1,251 @@
from .create_browser import *
import appdirs
import logging
# import pickle
import dill as pickle
import selenium.common.exceptions
import selenium.webdriver
import sys
import time
import traceback
import os
USER_AGENT = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)"
REFUSE_AFTER_EXCEPTION = True
EXECUTOR_PORT = 4444
REMOTE_EXECUTOR = 'http://127.0.0.1:%s/wd/hub'
# PICKLE_FILENAME = '/tmp/nhsnwebdriverdump'
# PICKLE_FILENAME = os.path.join(
# appdirs.user_data_dir('wabot'),
# 'saved_browser_instances.pickle'
# )
LOGGER = logging.getLogger('wabot')
DEFAULT_WEBDRIVER_TYPE = 'firefox1'
class BrowserProxy:
def __init__(
self,
session_name='webdriver',
pickle_filename=None,
phantom=False,
webdriver_type=None # remote_chromium2
):
"""
BrowserProxy wraps a selenium webdriver instance and provides utility
functions for automation webpages.
"""
LOGGER.info('requesting selenium browser instance (%s): instance_name = %s', webdriver_type, session_name)
# if pickle_filename is None:
# pickle_filename = PICKLE_FILENAME
# self._pickle_filename = pickle_filename
if webdriver_type is None:
webdriver_type = 'firefox1'
assert webdriver_type in (
'firefox1',
'firefox2',
'chromium2',
'remote_chromium',
'remote_chrome',
'phantomjs',
'remote_firefox'
), 'webdriver_type must be firefox1, firefox2, chromium2, remote_chromium2, or phantomjs'
try:
self.driver_type = webdriver_type
if phantom:
pass
# driver_type = "phantomjs"
self.driver = self.get_driver(webdriver_type, session_name)
if not self.driver:
LOGGER.error('failed to get selenium webdriver')
self.good = False
return
except Exception as ex:
print('caught exception at BrowserProxy().__init__')
print(type(ex), ex)
raise
# self.page = nhsn_lo.pages.Login(self)
# self.good = True
def get_page(self):
return type(self.page)
def set_page(self, page):
LOGGER.trace('switching page to %s' % page)
newpage = page(self)
if hasattr(newpage, 'verify'):
rc = newpage.verify()
if not rc:
LOGGER.error('failed to verify page: %s' % page)
# input('failure')
return False
else:
LOGGER.trace('verified page: %s' % page)
self.page = newpage
return True
def switch_after(self, fn=None, page=None):
if not fn:
return lambda fn: self.switch_after(fn, page = page)
def wrapper(*args, **kwargs):
self.set_page(page)
return fn(*args, **kwargs)
return wrapper
def __getattr__(self, name):
"""
Access method on current page.
When object.method is referenced, python invokes
object.__getattribute__ which checks the instance
for a matching property, If none is found, python
invokes object.__getattr__, which is overridden here.
If the attribute is not found in the main class, this
will search the page object for the method. This
allows invoking page methods through the proxy class.
nhsn.foo() invokes nhsn.page.foo()
"""
# todo(mg) would this be a good spot to inject page verification?
# idea: maybe we could decorate method with error checking and
# error collection? method can return value, rc and this can
# queue actions and error check them as it goes? At the end,
# the user would be able to pull out the errors and check them
if REFUSE_AFTER_EXCEPTION and not self.good:
LOGGER.warn("broken state - refusing to invoke page action: %s" % (name))
return
try:
method = getattr(self.page, name)
return method
except AttributeError as ex:
raise
def __getitem__(self, key):
"""
Access element on the current page.
"""
return self.page[key]
def perform(self, meth, *args, **kwargs):
"""
Calls self.page.method and catches exceptions.
"""
# input('<breakpoint> at perform')
if REFUSE_AFTER_EXCEPTION and not self.good:
LOGGER.warn("broken state - refusing to invoke page action: %s" % (meth))
return
try:
x = getattr(self.page, meth)
except AttributeError as ex:
LOGGER.error("Failed to invoke page action. "\
"page = %s, method = %s" %\
(self.page.__class__, meth))
return None
# Hooks:
# x = self.page.detect_logged_out()
# y = self.page.detected_logged_in()
# print('logged out/in: ', x, y)
LOGGER.trace("invoking %s.%s" % (self.page.__class__.__name__, meth))
try:
return x(*args, **kwargs)
except selenium.common.exceptions.NoSuchWindowException:
self.good = False
except Exception as ex:
print("caught exception: %s" % (type(ex)))
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value,
exc_traceback)
for line in lines:
print(line, end = "")
self.good = False
def reset_good_status(self):
self.good = True
def get_driver(self, browser, session_name='webdriver'):
"""
Returns selenium objects of different types
The combinations of selenium{1,2} {chrome,firefox} are supported.
In the case of selenium 1 (selenium server), the host is a constant
defined at the top of this file.
Args:
browser: The selenium webdriver requested.
If a chromium2 webdriver is requested, selenium
will try to use chromium as the browser. If
firefox2 is requested, selenium will try to use
firefox. Chromium is very fast but it is designed
to be fast for an interactive user which means it
is fast at the cost of processing power and
ram. Firefox is about as fast but uses less
resources.
Available browser drivers are:
chromium2, chromium1, firefox2, firefox1
The associated webdriver has to be installed
and runnable on the system.
Returns:
The selenium webdriver handle.
"""
LOGGER.debug('requesting selenium browser instance: type = %s' % (browser))
driver = None
browser_factory = CreateBrowser()
if browser == 'chromium2': # Selenium 2 - Chrome
driver = self._create_driver_chromium()
elif (
browser == 'remote_chromium'
or browser == 'remote_chrome'
):
driver = browser_factory._create_driver_remote_chromium(session_name)
elif browser == 'remote_firefox': # Selenium 1 - Firefox
driver = browser_factory._create_driver_remote_firefox(session_name)
# TODO(MG) need to redo/rename below. selenium2/ not remote, uses native browser
# webdrviers instead of javascript
elif browser == 'chromium1': # Selenium 1 - Chrome without working user agent switch
driver = self._create_driver_chromium1()
elif browser == 'firefox2': # Selenium 2 - Firefox
driver = self._create_driver_firefox2()
elif browser == 'phantomjs':
driver = self._create_driver_phantomjs()
else:
LOGGER.error(
'an attempt was made to request an '\
'unsupported (by this product) selenium '\
'webdriver; refusing. requested = %s'\
% (browser)
)
driver.implicitly_wait(10)
return driver

316
wabot/create_browser.py Normal file

@ -0,0 +1,316 @@
import appdirs
import datetime
import logging
import os
import selenium
import pickle
LOGGER = logging.getLogger('wabot')
USER_AGENT = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)"
REFUSE_AFTER_EXCEPTION = True
EXECUTOR_PORT = 4444
REMOTE_EXECUTOR = 'http://127.0.0.1:%s/wd/hub'
class PickledBrowserReference:
"""
Structure for saving a webdriver instance. Also includes the timestamp
to help invalidate old references.
"""
def __init__(self):
self.browser_ref = None
self.timestamp = None
# Factory Creator - instantiate Selenium1 and Selenium2 webdriver instances.
class CreateBrowser:
"""
Creates and instantiates selenium webbrowser instances.
Two strategies: remote via selenium server or local.
The advantage of using remote is the browser instance can stay
open after the process closes. This allows you to disconnect
and re-connect to the same browser instance with different
processes.
"""
def __init__(self, pickle_filename=None):
self._pickle_filename = None
if pickle_filename is not None:
self.pickle_filename = pickle_filename
self._has_cleaned = False
@property
def pickle_filename(self):
"""
Reference to the user-data-dir pickle target filename, for
saving browser references.
"""
if self._pickle_filename is None:
self._pickle_filename = os.path.join(
appdirs.user_data_dir('wabot'),
'saved_browser_instances.pickle'
)
return self._pickle_filename
else:
return self._pickle_filename
@pickle_filename.setter
def pickle_filename(self, value):
self._pickle_filename = value
def _clean_old_pickles(self):
if self._has_cleaned is True:
LOGGER.debug('not cleaning pickles twice')
return
p = self.pickle_filename
try:
fp = open(p, 'rb')
pickles = pickle.load(fp)
if not pickles:
raise Exception
LOGGER.info('cleaning any old pickle refs')
for pickle_ref in pickles:
driver_ref = pickles[pickle_ref]
ts = driver_ref.timestamp
if datetime.datetime.now() - driver_ref.timestamp >= datetime.timedelta(days=3):
LOGGER.info('deleting old pickled driver ref: %s at %s', pickle_ref, ts)
pickles.pop(final_name)
self._has_cleaned = True
except (FileNotFoundError, IOError) as ex:
LOGGER.error('unable to load pickles: no pickled drivers found')
except Exception as ex:
print(ex)
pass
def _load_from_pickle(self, final_name):
p = self.pickle_filename
# Definitely no browser instance already, we must instantiate
if not os.path.exists(p):
LOGGER.debug('no pickled file for saved browser instances (nothing saved yet)')
return
# There MAY be an open browser or an invalidated reference to a once-open browser
if os.path.exists(p):
LOGGER.debug('found pickled file for saved browser instances: %s', p)
# First, see if existing session_name browser instance exists
fp = None
self._clean_old_pickles()
try:
fp = open(p, 'rb')
# drivers = pickle.load(fp)
pickles = pickle.load(fp)
if not pickles:
raise Exception
for idx, saved_instance in enumerate(pickles):
LOGGER.debug('found saved browser instances (%s): %s', idx, saved_instance)
driver_ref = pickles.get(final_name)
if not driver_ref:
raise Exception
# if datetime.datetime.now() - driver_ref.timestamp >= datetime.timedelta(days=3):
# pickles.pop(final_name)
driver = driver_ref.browser_ref
LOGGER.debug('connected to pickled webdriver instance: %s', final_name)
url = driver.current_url # throw error if driver isn't reliable anymore
LOGGER.info('webdriver instance is ready')
# self.driver = driver
return driver
except (FileNotFoundError, IOError) as ex:
LOGGER.error('unable to connect to existing webdriver: no pickled drivers found')
except Exception as ex:
print(ex)
self.driver = None
def _save_to_pickle(self, final_name, driver_ref):
p = self.pickle_filename
pickles = {}
# Definitely no browser instance already, we must instantiate
if not os.path.exists(p):
LOGGER.debug('no pickled file for saved browser instances (nothing saved yet)')
# Create usr-app-dir if doesn't exist
dirname = os.path.dirname(p)
if not os.path.isdir(dirname):
os.mkdir(dirname)
# There MAY be an open browser or an invalidated reference to a once-open browser
if os.path.exists(p):
LOGGER.debug('found existing pickle file for saved browser instances: %s', p)
# First, see if existing session_name browser instance exists
self._clean_old_pickles()
fp = open(p, 'rb')
pickles = pickle.load(fp)
if not pickles:
raise Exception
LOGGER.debug('found saved browser instances while saving: %s', list(pickles.keys()))
fp.close()
# Save to pickle - creating file if necessary
pkl = PickledBrowserReference()
pkl.browser_ref = driver_ref
pkl.timestamp = datetime.datetime.now()
pickles[final_name] = pkl
import pprint;
print('pickles:')
pprint.pprint(pickles)
# pickle file must be created
fp = open(p, 'wb')
# drivers[final_name] = driver
LOGGER.info('saving browser instance to pickle: %s', final_name)
pickle.dump(pickles, fp)
fp.close()
def _create_driver_remote_chromium(self, session_name):
"""
Creates and returns Selenium1 chromium instance.
"""
p = self.pickle_filename
# e.g.: remote-chromium-webdriver
final_name = '{}-{}'.format('remote-chromium', session_name)
driver = None
driver = self._load_from_pickle(final_name)
if driver is not None:
LOGGER.info('webdriver instance is ready (from pickle)')
return driver
# At this point, need to instantiate a new browser instance
sel_host = REMOTE_EXECUTOR % (EXECUTOR_PORT)
LOGGER.info('instantianting new browser instance (remote_chromium)')
LOGGER.info('remote selenium: %s', sel_host)
opt = selenium.webdriver.chrome.options.Options()
opt.add_argument("--user-agent=" + USER_AGENT)
opt.add_argument("--kiosk-printing")
opt.add_argument("--focus-existing-tab-on-open=false")
driver = selenium.webdriver.Remote(
command_executor=sel_host,
desired_capabilities = opt.to_capabilities()
)
self._save_to_pickle(final_name, driver)
# self.driver = driver
return driver
# Pickle impl. is duped here
def _create_driver_remote_firefox(self, session_name):
"""
Creates and returns Selenium1 firefox instance.
"""
final_name = '{}-{}'.format('remote-firefox', session_name)
driver = None
driver = self._load_from_pickle(final_name)
if driver is not None:
LOGGER.info('webdriver instance is ready (from pickle)')
return driver
# At this point, need to instantiate a new browser instance
sel_host = REMOTE_EXECUTOR % (EXECUTOR_PORT)
LOGGER.info('instantianting new browser instance (remote_firefox)')
LOGGER.info('remote selenium: %s', sel_host)
profile = selenium.webdriver.FirefoxProfile()
profile.set_preference("general.useragent.override", USER_AGENT)
driver = selenium.webdriver.Remote(
# SELENIUM1_SERVER_PATH,
sel_host,
selenium.webdriver.DesiredCapabilities.FIREFOX.copy(),
browser_profile = profile
)
self._save_to_pickle(final_name, driver)
return driver
def _create_driver_chromium2(self):
opt = selenium.webdriver.chrome.options.Options()
opt.add_argument("--user-agent=" + USER_AGENT)
opt.add_argument("--kiosk-printing")
driver = selenium.webdriver.Chrome(chrome_options = opt)
self.driver = driver
return driver
def _create_driver_chromium1(self):
# Selenium 1 - Chrome without working user agent switch
# These two methods of creation ChromeOptions are equivalent objects
options = selenium.webdriver.ChromeOptions()
options.add_argument("--user-agent=" + USER_AGENT)
driver = selenium.webdriver.Remote(desired_capabilities = options.to_capabilities())
driver = selenium.webdriver.Remote(SELENIUM1_SERVER_PATH,
selenium.webdriver.DesiredCapabilities.CHROME.copy())
return driver
def _create_driver_firefox2(self):
# tmp = selenium.webdriver.FirefoxProfile()
# tmp = None
profile = None
# filename = "/tmp/firefox_profile"
# try:
# fp = open(filename, "rb")
# profile = pickle.load(fp)
# except:
# pass
if not profile:
profile = selenium.webdriver.FirefoxProfile(profile_directory = "/home/mathew/firefox_prof")
profile.set_preference("general.useragent.override", USER_AGENT )
profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/csv");
profile.set_preference("network.http.redirection-limit", "0" )
# profile.set_preference("javascript.enabled", False )
# profile.set_preference("print.always_print_silent", True)
profile.set_preference("print.print_to_file", True)
profile.set_preference("print.print_to_filename", "/tmp/print.pdf")
profile.update_preferences()
profile.set_preference("network.http.redirection-limit", "0" )
# with open("/tmp/firefox_profile", "wb") as fp:
# pickle.dump(profile, fp, pickle.HIGHEST_PROTOCOL)
# driver = selenium.webdriver.Firefox()
driver = selenium.webdriver.Firefox(profile)
return driver
def _create_driver_phantomjs(self):
# Note(MG): Selenium support for PhantomJS has been deprecated, please use headless
# driver = selenium.webdriver.PhantomJS()
# return driver
opt = selenium.webdriver.chrome.options.Options()
opt.add_argument("--user-agent=" + USER_AGENT)
opt.add_argument("--kiosk-printing")
opt.add_argument("--headless")
driver = selenium.webdriver.Chrome(chrome_options = opt)
driver.set_window_size(838, 907)
self.driver = driver
return driver

@ -1,3 +1,10 @@
import logging
import random
import selenium
import time
LOGGER = logging.getLogger('wabot')
class PageObject: class PageObject:
""" """
Wrapper around page element that provides an object orientated interface Wrapper around page element that provides an object orientated interface
@ -53,7 +60,7 @@ class TextField(PageObject):
return self.page.get_el_value(self.el) return self.page.get_el_value(self.el)
def set_value(self, value): def set_value(self, value):
nhsn_lo.logger.info('<%s> set_text (%s)' % (self.name, value)) LOGGER.info('[%s]->set_text (%s)' % (self.name, value))
time.sleep(random.uniform(3,5)) time.sleep(random.uniform(3,5))
return self.page.set_el_value(self.el, value) return self.page.set_el_value(self.el, value)
@ -69,7 +76,7 @@ class SelectField(PageObject):
return self.page.get_select_value(self.dropdown) return self.page.get_select_value(self.dropdown)
def set_value(self, value): def set_value(self, value):
nhsn_lo.logger.info('<%s> set_select (%s)' % (self.name, value)) LOGGER.info('[%s]->set_select (%s)' % (self.name, value))
time.sleep(random.uniform(6,11)) time.sleep(random.uniform(6,11))
return self.page.set_select_value(self.dropdown, value) return self.page.set_select_value(self.dropdown, value)
@ -87,7 +94,7 @@ class CheckField(PageObject):
return self.page.get_checkbox_value(self.el, ignore) return self.page.get_checkbox_value(self.el, ignore)
def set_checked(self, checked): def set_checked(self, checked):
nhsn_lo.logger.info('<%s> set_checked (%s)' % (self.name, checked)) LOGGER.info('[%s]->set_checked (%s)' % (self.name, checked))
time.sleep(random.uniform(2,3)) time.sleep(random.uniform(2,3))
return self.page.set_checkbox(self.el, checked) return self.page.set_checkbox(self.el, checked)

@ -1,5 +1,3 @@
import nhsn_lo.pages
import enum import enum
import random import random
import selenium.webdriver import selenium.webdriver
@ -15,6 +13,9 @@ from PIL import Image
import os import os
import inspect import inspect
import logging
LOGGER = logging.getLogger('wabot')
ENABLE_GOTO_CLINIC_SELECT_OPTIMIZATION = True ENABLE_GOTO_CLINIC_SELECT_OPTIMIZATION = True
ALERT_TIMEOUT = 3 ALERT_TIMEOUT = 3
@ -33,7 +34,6 @@ class RC(enum.Enum):
NO_FACILITY = 4 NO_FACILITY = 4
MISSING_REPORTING_PLAN = 5 MISSING_REPORTING_PLAN = 5
class Page: class Page:
""" """
Provides ancillary utility methods such as finding Provides ancillary utility methods such as finding
@ -42,6 +42,7 @@ class Page:
def __init__(self, parent): def __init__(self, parent):
self.parent = parent self.parent = parent
self.driver = parent.driver self.driver = parent.driver
LOGGER.info("Loaded Page() '%s'", self.__class__.__name__)
def find_element_locators(self, key): def find_element_locators(self, key):
""" """
@ -63,14 +64,14 @@ class Page:
def get_proxy(self, key): def get_proxy(self, key):
locators = self.find_element_locators(key) locators = self.find_element_locators(key)
if not locators: if not locators:
nhsn_lo.logger.warn('element not found: %s' % (key)) LOGGER.warn('element not found: %s' % (key))
return NullField(None, page=self) return NullField(None, page=self)
obj_type = locators[0] obj_type = locators[0]
accessors = locators[1] accessors = locators[1]
# if not el: # if not el:
# nhsn_lo.logger.warn('failed to make proxy object: %s' % key) # LOGGER.warn('failed to make proxy object: %s' % key)
# return NullField(None, page=self) # return NullField(None, page=self)
if obj_type == 'el': if obj_type == 'el':
@ -93,9 +94,9 @@ class Page:
name=key) name=key)
return obj return obj
else: else:
nhsn_lo.logger.error('failed to create page element type: %s' % (obj_type)) LOGGER.error('failed to create page element type: %s' % (obj_type))
nhsn_lo.logger.error('requested unknown page element: %s' % key) LOGGER.error('requested unknown page element: %s' % key)
return return
def __getitem__(self, key): def __getitem__(self, key):
@ -109,7 +110,7 @@ class Page:
""" """
locators = self.find_element_locators(key) locators = self.find_element_locators(key)
if not locators: if not locators:
nhsn_lo.logger.error('failed to find page element: %s' % (key)) LOGGER.error('failed to find page element: %s' % (key))
return False return False
try: try:
@ -133,35 +134,35 @@ class Page:
by = locator[0] by = locator[0]
value = locator[1] value = locator[1]
el = self.driver.find_element(by=by, value=value) el = self.driver.find_element(by=by, value=value)
nhsn_lo.logger.trace('found single element (%s) by %s = %s', key, by, value) LOGGER.trace('found single element (%s) by %s = %s', key, by, value)
return el return el
except Exception as ex: except Exception as ex:
nhsn_lo.logger.warn('failed to find single element (%s) by %s = %s', key, by, value) LOGGER.warn('failed to find single element (%s) by %s = %s', key, by, value)
continue continue
elif type_ == 'els': elif type_ == 'els':
try: try:
by = locator[0] by = locator[0]
value = locator[1] value = locator[1]
els = self.driver.find_elements(by=by, value=value) els = self.driver.find_elements(by=by, value=value)
nhsn_lo.logger.trace('found elements (%s) by %s = %s', key, by, value) LOGGER.trace('found elements (%s) by %s = %s', key, by, value)
return els return els
except Exception as ex: except Exception as ex:
nhsn_lo.logger.warn('failed to find any elements (%s) by %s = %s', key, by, value) LOGGER.warn('failed to find any elements (%s) by %s = %s', key, by, value)
continue continue
elif type_ == 'select': elif type_ == 'select':
try: try:
by = locator[0] by = locator[0]
value = locator[1] value = locator[1]
el = self.driver.find_element(by=by, value=value) el = self.driver.find_element(by=by, value=value)
nhsn_lo.logger.trace('found dropdown element (%s) by %s = %s', key, by, value) LOGGER.trace('found dropdown element (%s) by %s = %s', key, by, value)
dropdown = selenium.webdriver.support.ui.Select(el) dropdown = selenium.webdriver.support.ui.Select(el)
return dropdown return dropdown
except Exception as ex: except Exception as ex:
nhsn_lo.logger.warn('failed to find dropdown (%s) by %s = %s', key, by, value) LOGGER.warn('failed to find dropdown (%s) by %s = %s', key, by, value)
continue continue
else: else:
nhsn_lo.logger.error('unable to find element (%s): unknown type = %s' % (key, type_)) LOGGER.error('unable to find element (%s): unknown type = %s' % (key, type_))
return return
except Exception as ex: except Exception as ex:
print(ex) print(ex)
@ -178,10 +179,10 @@ class Page:
if not rc: if not rc:
return rc return rc
self.accept_alert() self.accept_alert()
nhsn_lo.logger.debug('waiting for page to load...') LOGGER.debug('waiting for page to load...')
rc = self._wait_for_element_to_go_stale(el) rc = self._wait_for_element_to_go_stale(el)
if not rc: if not rc:
nhsn_lo.logger.error('failed: timed out waiting for page load') LOGGER.error('failed: timed out waiting for page load')
return False return False
return rc return rc
@ -191,7 +192,7 @@ class Page:
wait.until(lambda driver: self.is_element_stale(el)) wait.until(lambda driver: self.is_element_stale(el))
return True return True
except selenium.common.exceptions.TimeoutException as ex: except selenium.common.exceptions.TimeoutException as ex:
nhsn_lo.logger.error('failed: timed out waiting for page load') LOGGER.error('failed: timed out waiting for page load')
return False return False
def click(self, el): def click(self, el):
@ -203,13 +204,13 @@ class Page:
where things are clicked at. where things are clicked at.
""" """
if not el: if not el:
nhsn_lo.logger.warn("refusing to click null element") LOGGER.warn("refusing to click null element")
return False return False
try: try:
size = el.size size = el.size
except selenium.common.exceptions.StaleElementReferenceException as ex: except selenium.common.exceptions.StaleElementReferenceException as ex:
nhsn_lo.logger.error('failed to click element: stale reference') LOGGER.error('failed to click element: stale reference')
return False return False
# Use a guassian distribution to click more often towards the center # Use a guassian distribution to click more often towards the center
@ -226,10 +227,10 @@ class Page:
el.click() el.click()
return True return True
except selenium.common.exceptions.ElementNotVisibleException as ex: except selenium.common.exceptions.ElementNotVisibleException as ex:
nhsn_lo.logger.error('failed to click element: not visible') LOGGER.error('failed to click element: not visible')
return False return False
except selenium.common.exceptions.StaleElementReferenceException as ex: except selenium.common.exceptions.StaleElementReferenceException as ex:
nhsn_lo.logger.error('failed to click element: stale reference') LOGGER.error('failed to click element: stale reference')
return False return False
time.sleep(.2) time.sleep(.2)
i = i+1 i = i+1
@ -244,7 +245,7 @@ class Page:
el.click() el.click()
return return
nhsn_lo.logger.trace( "clicking %s (dim: x = %s, y = %s) at %d, %d" % LOGGER.debug( "clicking %s (dim: x = %s, y = %s) at %d, %d" %
(self.get_el_identifier(el), size["width"], size["height"], x,y) ) (self.get_el_identifier(el), size["width"], size["height"], x,y) )
i = 0 i = 0
@ -257,9 +258,6 @@ class Page:
time.sleep(.5) time.sleep(.5)
i = i + 1 i = i + 1
# return el.click() # return el.click()
x = int(x) x = int(x)
@ -273,12 +271,12 @@ class Page:
actions.perform() actions.perform()
except Exception as ex: # type is selenium timeout... not sure except Exception as ex: # type is selenium timeout... not sure
print(ex) print(ex)
nhsn_lo.pages.logger.error("%s" % (ex)) LOGGER.error("%s" % (ex))
return False return False
return True return True
def save_screenshot(self, filename): def save_screenshot(self, filename):
nhsn_lo.pages.logger.info("saving page screenshot: %s" % (filename)) LOGGER.info("saving page screenshot: %s" % (filename))
# Chromium2 screenshot only captures viewable area, # Chromium2 screenshot only captures viewable area,
@ -379,7 +377,7 @@ class Page:
if not el: if not el:
return None return None
val = el.get_attribute("value") val = el.get_attribute("value")
# nhsn_lo.logger.debug('peeked at field <%s>, value = %s' # LOGGER.debug('peeked at field <%s>, value = %s'
# % (self.get_el_identifier(el), val)) # % (self.get_el_identifier(el), val))
return val return val
@ -408,13 +406,13 @@ class Page:
el.send_keys(ch) el.send_keys(ch)
time.sleep(1) time.sleep(1)
except Exception as ex: except Exception as ex:
nhsn_lo.pages.logger.error("failed to send keys, element in unknown state!!: %s" % (ex)) LOGGER.error("failed to send keys, element in unknown state!!: %s" % (ex))
return False return False
else: else:
try: try:
el.send_keys(value) el.send_keys(value)
except Exception as ex: except Exception as ex:
nhsn_lo.pages.logger.error("failed to send keys, element in unknown state!!: %s" % (ex)) LOGGER.error("failed to send keys, element in unknown state!!: %s" % (ex))
return False return False
el_value = self.get_el_value(el) el_value = self.get_el_value(el)
@ -422,13 +420,13 @@ class Page:
print(type(el_value), type(value)) print(type(el_value), type(value))
print("values didn't match.", value, el_value) print("values didn't match.", value, el_value)
return False return False
# nhsn_lo.logger.info('set field <%s> -> %s; previous = %s' # LOGGER.info('set field <%s> -> %s; previous = %s'
# % (self.get_el_identifier(el), el_value, prev_val)) # % (self.get_el_identifier(el), el_value, prev_val))
return True return True
def get_select_value(self, select): def get_select_value(self, select):
if not select: if not select:
nhsn_lo.pages.logger.error("tried to get select value of NULL element") LOGGER.error("tried to get select value of NULL element")
return None return None
try: try:
value = select.first_selected_option.get_attribute("value") value = select.first_selected_option.get_attribute("value")
@ -439,7 +437,7 @@ class Page:
def set_select_value(self, select, value=None, text=None): def set_select_value(self, select, value=None, text=None):
if not select: if not select:
return False return False
# nhsn_lo.pages.logger.trace("setting select value (%s) for (%s)"\ # LOGGER.trace("setting select value (%s) for (%s)"\
# % (value, self.get_el_identifier(select._el))) # % (value, self.get_el_identifier(select._el)))
if value: if value:
try: try:
@ -461,11 +459,11 @@ class Page:
return False return False
if checked: if checked:
if not is_selected: if not is_selected:
# nhsn_lo.pages.logger.trace("checking unchecked box (%s)" % (self.get_el_identifier(el))) # LOGGER.trace("checking unchecked box (%s)" % (self.get_el_identifier(el)))
self.click(el) self.click(el)
else: else:
if is_selected: if is_selected:
# nhsn_lo.pages.logger.trace("unchecking checked box (%s)" % (self.get_el_identifier(el))) # LOGGER.trace("unchecking checked box (%s)" % (self.get_el_identifier(el)))
self.click(el) self.click(el)
val = self.get_checkbox_value(el) val = self.get_checkbox_value(el)
if val != checked: if val != checked:
@ -516,10 +514,10 @@ class Page:
alert.accept() alert.accept()
else: else:
alert.dismiss() alert.dismiss()
nhsn_lo.pages.logger.trace('caught js alert: %s' % text) LOGGER.trace('caught js alert: %s' % text)
return text return text
except selenium.common.exceptions.TimeoutException: except selenium.common.exceptions.TimeoutException:
nhsn_lo.pages.logger.trace('no js alert present') LOGGER.trace('no js alert present')
return False return False
def is_element_stale(self, webelement): def is_element_stale(self, webelement):