1import logging 2import time 3from autotest_lib.client.common_lib import error 4from autotest_lib.client.common_lib import utils 5 6 7class UI_Handler(object): 8 9 REGEX_ALL = '/(.*?)/' 10 11 PROMISE_TEMPLATE = \ 12 '''new Promise(function(resolve, reject) { 13 chrome.automation.getDesktop(function(root) { 14 resolve(%s); 15 }) 16 })''' 17 18 def start_ui_root(self, cr): 19 """Start the UI root object for testing.""" 20 self.ext = cr.autotest_ext 21 22 def is_obj_restricted(self, name, isRegex=False, role=None): 23 """ 24 Return True if the object restriction is 'disabled'. 25 This usually means the button is either greyed out, locked, etc. 26 27 @param name: Parameter to provide to the 'name' attribute. 28 @param isRegex: bool, if the item is a regex. 29 @param role: Parameter to provide to the 'role' attribute. 30 31 """ 32 FindParams = self._get_FindParams_str(name=name, 33 role=role, 34 isRegex=isRegex) 35 try: 36 restriction = self.ext.EvaluateJavaScript( 37 self.PROMISE_TEMPLATE % ("%s.restriction" % FindParams), 38 promise=True) 39 except Exception: 40 raise error.TestError( 41 'Could not find object {}.'.format(name)) 42 if restriction == 'disabled': 43 return True 44 return False 45 46 def item_present(self, name, isRegex=False, flip=False, role=None): 47 """ 48 Determines if an object is present on the screen 49 50 @param name: Parameter to provide to the 'name' attribute. 51 @param isRegex: bool, if the 'name' is a regex. 52 @param flip: Flips the return status. 53 @param role: Parameter to provide to the 'role' attribute. 54 55 @returns: 56 True if object is present and flip is False. 57 False if object is present and flip is True. 58 False if object is not present and flip is False. 59 True if object is not present and flip is True. 60 61 """ 62 FindParams = self._get_FindParams_str(name=name, 63 role=role, 64 isRegex=isRegex) 65 66 if flip is True: 67 return not self._is_item_present(FindParams) 68 return self._is_item_present(FindParams) 69 70 def wait_for_ui_obj(self, 71 name, 72 isRegex=False, 73 remove=False, 74 role=None, 75 timeout=10): 76 """ 77 Waits for the UI object specified. 78 79 @param name: Parameter to provide to the 'name' attribute. 80 @param isRegex: bool, if the 'name' is a regex. 81 @param remove: bool, if you are waiting for the item to be removed. 82 @param role: Parameter to provide to the 'role' attribute. 83 @param timeout: int, time to wait for the item. 84 85 @raises error.TestError if the element is not loaded (or removed). 86 87 """ 88 utils.poll_for_condition( 89 condition=lambda: self.item_present(name=name, 90 isRegex=isRegex, 91 flip=remove, 92 role=role), 93 timeout=timeout, 94 exception=error.TestError('{} did not load in: {}' 95 .format(name, self.list_screen_items()))) 96 97 def did_obj_not_load(self, name, isRegex=False, timeout=5): 98 """ 99 Specifically used to wait and see if an item appears on the UI. 100 101 NOTE: This is different from wait_for_ui_obj because that returns as 102 soon as the object is either loaded or not loaded. This function will 103 wait to ensure over the timeout period the object never loads. 104 Additionally it will return as soon as it does load. Basically a fancy 105 time.sleep() 106 107 @param name: Parameter to provide to the 'name' attribute. 108 @param isRegex: bool, if the item is a regex. 109 @param timeout: Time in seconds to wait for the object to appear. 110 111 @returns: True if object never loaded within the timeout period, 112 else False. 113 114 """ 115 t1 = time.time() 116 while time.time() - t1 < timeout: 117 if self.item_present(name=name, isRegex=isRegex): 118 return False 119 time.sleep(1) 120 return True 121 122 def doDefault_on_obj(self, name, isRegex=False, role=None): 123 """Runs the .doDefault() js command on the element.""" 124 FindParams = self._get_FindParams_str(name=name, 125 role=role, 126 isRegex=isRegex) 127 try: 128 self.ext.EvaluateJavaScript( 129 self.PROMISE_TEMPLATE % ("%s.doDefault()" % FindParams), 130 promise=True) 131 except: 132 logging.info('Unable to .doDefault() on {}. All items: {}' 133 .format(FindParams, self.list_screen_items())) 134 raise error.TestError("doDefault failed on {}".format(FindParams)) 135 136 def doCommand_on_obj(self, name, cmd, isRegex=False, role=None): 137 """Run the specified command on the element.""" 138 FindParams = self._get_FindParams_str(name=name, 139 role=role, 140 isRegex=isRegex) 141 return self.ext.EvaluateJavaScript(self.PROMISE_TEMPLATE % """ 142 %s.%s""" % (FindParams, cmd), promise=True) 143 144 def list_screen_items(self, 145 role=None, 146 name=None, 147 isRegex=False, 148 attr='name'): 149 150 """ 151 List all the items currently visable on the screen. 152 153 If no paramters are given, it will return the name of each item, 154 including items with empty names. 155 156 @param role: The role of the items to use (ie button). 157 @param name: Parameter to provide to the 'name' attribute. 158 @param isRegex: bool, if the obj is a regex. 159 @param attr: Str, the attribute you want returned in the list 160 (eg 'name'). 161 162 """ 163 164 if isRegex: 165 if name is None: 166 raise error.TestError('If regex is True name must be given') 167 name = self._format_obj(name, isRegex) 168 elif name is not None: 169 name = self._format_obj(name, isRegex) 170 name = self.REGEX_ALL if name is None else name 171 role = self.REGEX_ALL if role is None else self._format_obj(role, 172 False) 173 174 new_promise = self.PROMISE_TEMPLATE % """root.findAll({attributes: 175 {name: %s, role: %s}}).map(node => node.%s)""" % (name, role, attr) 176 177 return self.ext.EvaluateJavaScript(new_promise, promise=True) 178 179 def get_name_role_list(self): 180 """ 181 Return [{}, {}] containing the name/role of everything on screen. 182 183 """ 184 combined = [] 185 names = self.list_screen_items(attr='name') 186 roles = self.list_screen_items(attr='role') 187 188 if len(names) != len(roles): 189 raise error.TestError('Number of items in names and roles !=') 190 191 for name, role in zip(names, roles): 192 temp_d = {'name': name, 'role': role} 193 combined.append(temp_d) 194 return combined 195 196 def _format_obj(self, name, isRegex): 197 """ 198 Formats the object for use in the javascript name attribute. 199 200 When searching for an element on the UI, a regex expression or string 201 can be used. If the search is using a string, the obj will need to be 202 wrapped in quotes. A Regex is not. 203 204 @param name: Parameter to provide to the 'name' attribute. 205 @param isRegex: if True, the object will be returned as is, if False 206 the obj will be returned wrapped in quotes. 207 208 @returns: The formatted string for regex/name. 209 """ 210 if isRegex: 211 return name 212 else: 213 return '"{}"'.format(name) 214 215 def _get_FindParams_str(self, name, role, isRegex): 216 """Returns the FindParms string, so that automation node functions 217 can be run on it 218 219 @param role: The role of the items to use (ie button). 220 @param name: Parameter to provide to the 'name' attribute. 221 @param isRegex: bool, if the obj is a regex. 222 223 @returns: The ".find($FindParams)" string, which can be used to run 224 automation node commands, such as .doDefault() 225 226 """ 227 FINDPARAMS_BASE = """ 228 root.find({attributes: 229 {name: %s, 230 role: %s}} 231 )""" 232 233 name = self._format_obj(name, isRegex) 234 if role is None: 235 role = self.REGEX_ALL 236 else: 237 role = self._format_obj(role, False) 238 return (FINDPARAMS_BASE % (name, role)) 239 240 def _is_item_present(self, findParams): 241 """Return False if tempVar is None, else True.""" 242 item_present = self.ext.EvaluateJavaScript( 243 self.PROMISE_TEMPLATE % findParams, 244 promise=True) 245 if item_present is None: 246 return False 247 return True 248 249 def click_and_wait_for_item_with_retries(self, 250 item_to_click, 251 item_to_wait_for, 252 isRegex_click=False, 253 isRegex_wait=False, 254 click_role=None, 255 wait_role=None): 256 """ 257 Click on an item, and wait for a subsequent item to load. If the new 258 item does not load, we attempt to click the button again. 259 260 This being done to remove the corner case of button being visually 261 loaded, but not fully ready to be clicked yet. In simple terms: 262 Click button --> Check for next button to appear 263 IF button does not appear, its likely the original button click did 264 not work, thus reclick that button --> recheck for the next button. 265 266 If button did appear, stop clicking. 267 268 """ 269 self.doDefault_on_obj(item_to_click, 270 role=click_role, 271 isRegex=isRegex_click) 272 for retry in xrange(3): 273 try: 274 self.wait_for_ui_obj(item_to_wait_for, 275 role=wait_role, 276 isRegex=isRegex_wait, 277 timeout=6) 278 break 279 except error.TestError: 280 self.doDefault_on_obj(item_to_click, 281 role=click_role, 282 isRegex=isRegex_click) 283 else: 284 raise error.TestError('Item {} did not load after 2 tries'.format( 285 item_to_wait_for)) 286