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.
320 lines
12 KiB
320 lines
12 KiB
# Lint as: python2, python3
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import logging
|
|
import os
|
|
from six.moves import range
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib import utils
|
|
|
|
|
|
class UIScreenshoter(object):
|
|
"""Simple class to take screenshots within the ui_utils framework."""
|
|
|
|
_SCREENSHOT_DIR_PATH = '/var/log/ui_utils'
|
|
_SCREENSHOT_BASENAME = 'ui-screenshot'
|
|
|
|
def __init__(self):
|
|
if not os.path.exists(self._SCREENSHOT_DIR_PATH):
|
|
os.mkdir(self._SCREENSHOT_DIR_PATH, 0o755)
|
|
self.screenshot_num = 0
|
|
|
|
def take_ss(self):
|
|
try:
|
|
utils.run('screenshot "{}/{}_iter{}.png"'.format(
|
|
self._SCREENSHOT_DIR_PATH, self._SCREENSHOT_BASENAME,
|
|
self.screenshot_num))
|
|
self.screenshot_num += 1
|
|
except Exception as e:
|
|
logging.warning('Unable to capture screenshot. %s', e)
|
|
|
|
class UI_Handler(object):
|
|
|
|
REGEX_ALL = '/(.*?)/'
|
|
|
|
PROMISE_TEMPLATE = \
|
|
'''new Promise(function(resolve, reject) {
|
|
chrome.automation.getDesktop(function(root) {
|
|
resolve(%s);
|
|
})
|
|
})'''
|
|
|
|
def __init__(self):
|
|
self.screenshoter = UIScreenshoter()
|
|
|
|
def start_ui_root(self, cr):
|
|
"""Start the UI root object for testing."""
|
|
self.ext = cr.autotest_ext
|
|
|
|
def is_obj_restricted(self, name, isRegex=False, role=None):
|
|
"""
|
|
Return True if the object restriction is 'disabled'.
|
|
This usually means the button is either greyed out, locked, etc.
|
|
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the item is a regex.
|
|
@param role: Parameter to provide to the 'role' attribute.
|
|
|
|
"""
|
|
FindParams = self._get_FindParams_str(name=name,
|
|
role=role,
|
|
isRegex=isRegex)
|
|
try:
|
|
restriction = self.ext.EvaluateJavaScript(
|
|
self.PROMISE_TEMPLATE % ("%s.restriction" % FindParams),
|
|
promise=True)
|
|
except Exception:
|
|
raise error.TestError(
|
|
'Could not find object {}.'.format(name))
|
|
if restriction == 'disabled':
|
|
return True
|
|
return False
|
|
|
|
def item_present(self, name, isRegex=False, flip=False, role=None):
|
|
"""
|
|
Determines if an object is present on the screen
|
|
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the 'name' is a regex.
|
|
@param flip: Flips the return status.
|
|
@param role: Parameter to provide to the 'role' attribute.
|
|
|
|
@returns:
|
|
True if object is present and flip is False.
|
|
False if object is present and flip is True.
|
|
False if object is not present and flip is False.
|
|
True if object is not present and flip is True.
|
|
|
|
"""
|
|
FindParams = self._get_FindParams_str(name=name,
|
|
role=role,
|
|
isRegex=isRegex)
|
|
|
|
if flip is True:
|
|
return not self._is_item_present(FindParams)
|
|
return self._is_item_present(FindParams)
|
|
|
|
def wait_for_ui_obj(self,
|
|
name,
|
|
isRegex=False,
|
|
remove=False,
|
|
role=None,
|
|
timeout=10):
|
|
"""
|
|
Waits for the UI object specified.
|
|
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the 'name' is a regex.
|
|
@param remove: bool, if you are waiting for the item to be removed.
|
|
@param role: Parameter to provide to the 'role' attribute.
|
|
@param timeout: int, time to wait for the item.
|
|
|
|
@raises error.TestError if the element is not loaded (or removed).
|
|
|
|
"""
|
|
try:
|
|
utils.poll_for_condition(
|
|
condition=lambda: self.item_present(name=name,
|
|
isRegex=isRegex,
|
|
flip=remove,
|
|
role=role),
|
|
timeout=timeout,
|
|
exception=error.TestError('{} did not load'
|
|
.format(name)))
|
|
except error.TestError:
|
|
self.screenshoter.take_ss()
|
|
logging.debug("CURRENT UI ITEMS VISIBLE {}".format(
|
|
self.list_screen_items()))
|
|
raise error.TestError('{} did not load'.format(name))
|
|
|
|
def did_obj_not_load(self, name, isRegex=False, timeout=5):
|
|
"""
|
|
Specifically used to wait and see if an item appears on the UI.
|
|
|
|
NOTE: This is different from wait_for_ui_obj because that returns as
|
|
soon as the object is either loaded or not loaded. This function will
|
|
wait to ensure over the timeout period the object never loads.
|
|
Additionally it will return as soon as it does load. Basically a fancy
|
|
time.sleep()
|
|
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the item is a regex.
|
|
@param timeout: Time in seconds to wait for the object to appear.
|
|
|
|
@returns: True if object never loaded within the timeout period,
|
|
else False.
|
|
|
|
"""
|
|
t1 = time.time()
|
|
while time.time() - t1 < timeout:
|
|
if self.item_present(name=name, isRegex=isRegex):
|
|
return False
|
|
time.sleep(1)
|
|
return True
|
|
|
|
def doDefault_on_obj(self, name, isRegex=False, role=None):
|
|
"""Runs the .doDefault() js command on the element."""
|
|
FindParams = self._get_FindParams_str(name=name,
|
|
role=role,
|
|
isRegex=isRegex)
|
|
try:
|
|
self.ext.EvaluateJavaScript(
|
|
self.PROMISE_TEMPLATE % ("%s.doDefault()" % FindParams),
|
|
promise=True)
|
|
except:
|
|
logging.info('Unable to .doDefault() on {}. All items: {}'
|
|
.format(FindParams, self.list_screen_items()))
|
|
raise error.TestError("doDefault failed on {}".format(FindParams))
|
|
|
|
def doCommand_on_obj(self, name, cmd, isRegex=False, role=None):
|
|
"""Run the specified command on the element."""
|
|
FindParams = self._get_FindParams_str(name=name,
|
|
role=role,
|
|
isRegex=isRegex)
|
|
return self.ext.EvaluateJavaScript(self.PROMISE_TEMPLATE % """
|
|
%s.%s""" % (FindParams, cmd), promise=True)
|
|
|
|
def list_screen_items(self,
|
|
role=None,
|
|
name=None,
|
|
isRegex=False,
|
|
attr='name'):
|
|
|
|
"""
|
|
List all the items currently visable on the screen.
|
|
|
|
If no paramters are given, it will return the name of each item,
|
|
including items with empty names.
|
|
|
|
@param role: The role of the items to use (ie button).
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the obj is a regex.
|
|
@param attr: Str, the attribute you want returned in the list
|
|
(eg 'name').
|
|
|
|
"""
|
|
|
|
if isRegex:
|
|
if name is None:
|
|
raise error.TestError('If regex is True name must be given')
|
|
name = self._format_obj(name, isRegex)
|
|
elif name is not None:
|
|
name = self._format_obj(name, isRegex)
|
|
name = self.REGEX_ALL if name is None else name
|
|
role = self.REGEX_ALL if role is None else self._format_obj(role,
|
|
False)
|
|
|
|
new_promise = self.PROMISE_TEMPLATE % """root.findAll({attributes:
|
|
{name: %s, role: %s}}).map(node => node.%s)""" % (name, role, attr)
|
|
return self.ext.EvaluateJavaScript(new_promise, promise=True)
|
|
|
|
def get_name_role_list(self, name=None, role=None):
|
|
"""
|
|
Return [{}, {}] containing the name/role of everything on screen.
|
|
|
|
"""
|
|
name = self.REGEX_ALL if name is None else name
|
|
role = self.REGEX_ALL if role is None else self._format_obj(role,
|
|
False)
|
|
|
|
new_promise = self.PROMISE_TEMPLATE % """root.findAll({attributes:
|
|
{name: %s, role: %s}}).map(node =>
|
|
{return {name: node.name, role: node.role} })""" % (name, role)
|
|
|
|
return self.ext.EvaluateJavaScript(new_promise, promise=True)
|
|
|
|
def _format_obj(self, name, isRegex):
|
|
"""
|
|
Formats the object for use in the javascript name attribute.
|
|
|
|
When searching for an element on the UI, a regex expression or string
|
|
can be used. If the search is using a string, the obj will need to be
|
|
wrapped in quotes. A Regex is not.
|
|
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: if True, the object will be returned as is, if False
|
|
the obj will be returned wrapped in quotes.
|
|
|
|
@returns: The formatted string for regex/name.
|
|
"""
|
|
if isRegex:
|
|
return name
|
|
else:
|
|
return '"{}"'.format(name)
|
|
|
|
def _get_FindParams_str(self, name, role, isRegex):
|
|
"""Returns the FindParms string, so that automation node functions
|
|
can be run on it
|
|
|
|
@param role: The role of the items to use (ie button).
|
|
@param name: Parameter to provide to the 'name' attribute.
|
|
@param isRegex: bool, if the obj is a regex.
|
|
|
|
@returns: The ".find($FindParams)" string, which can be used to run
|
|
automation node commands, such as .doDefault()
|
|
|
|
"""
|
|
FINDPARAMS_BASE = """
|
|
root.find({attributes:
|
|
{name: %s,
|
|
role: %s}}
|
|
)"""
|
|
|
|
name = self._format_obj(name, isRegex)
|
|
if role is None:
|
|
role = self.REGEX_ALL
|
|
else:
|
|
role = self._format_obj(role, False)
|
|
return (FINDPARAMS_BASE % (name, role))
|
|
|
|
def _is_item_present(self, findParams):
|
|
"""Return False if tempVar is None, else True."""
|
|
item_present = self.ext.EvaluateJavaScript(
|
|
self.PROMISE_TEMPLATE % findParams,
|
|
promise=True)
|
|
if item_present is None:
|
|
return False
|
|
return True
|
|
|
|
def click_and_wait_for_item_with_retries(self,
|
|
item_to_click,
|
|
item_to_wait_for,
|
|
isRegex_click=False,
|
|
isRegex_wait=False,
|
|
click_role=None,
|
|
wait_role=None):
|
|
"""
|
|
Click on an item, and wait for a subsequent item to load. If the new
|
|
item does not load, we attempt to click the button again.
|
|
|
|
This being done to remove the corner case of button being visually
|
|
loaded, but not fully ready to be clicked yet. In simple terms:
|
|
Click button --> Check for next button to appear
|
|
IF button does not appear, its likely the original button click did
|
|
not work, thus reclick that button --> recheck for the next button.
|
|
|
|
If button did appear, stop clicking.
|
|
|
|
"""
|
|
self.doDefault_on_obj(item_to_click,
|
|
role=click_role,
|
|
isRegex=isRegex_click)
|
|
for retry in range(3):
|
|
try:
|
|
self.wait_for_ui_obj(item_to_wait_for,
|
|
role=wait_role,
|
|
isRegex=isRegex_wait,
|
|
timeout=6)
|
|
break
|
|
except error.TestError:
|
|
self.doDefault_on_obj(item_to_click,
|
|
role=click_role,
|
|
isRegex=isRegex_click)
|
|
else:
|
|
raise error.TestError('Item {} did not load after 2 tries'.format(
|
|
item_to_wait_for))
|