| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import types | |
| 6 | |
| 7 import selenium.common.exceptions | |
| 8 from selenium.webdriver.common.action_chains import ActionChains | |
| 9 from selenium.webdriver.support.ui import WebDriverWait | |
| 10 | |
| 11 | |
| 12 def _FocusField(driver, list_elem, field_elem): | |
| 13 """Focuses a field in a dynamic list. | |
| 14 | |
| 15 Note, the item containing the field should not be focused already. | |
| 16 | |
| 17 Typing into a field is tricky because the js automatically focuses and | |
| 18 selects the text field after 50ms after it first receives focus. This | |
| 19 method focuses the field and waits for the timeout to occur. | |
| 20 For more info, see inline_editable_list.js and search for setTimeout. | |
| 21 See crbug.com/97369. | |
| 22 | |
| 23 Args: | |
| 24 list_elem: An element in the HTML list. | |
| 25 field_elem: An element in the HTML text field. | |
| 26 | |
| 27 Raises: | |
| 28 RuntimeError: If a timeout occurs when waiting for the focus event. | |
| 29 """ | |
| 30 # To wait properly for the focus, we focus the last text field, and then | |
| 31 # add a focus listener to it, so that we return when the element is focused | |
| 32 # again after the timeout. We have to focus a different element in between | |
| 33 # these steps, otherwise the focus event will not fire since the element | |
| 34 # already has focus. | |
| 35 # Ideally this should be fixed in the page. | |
| 36 | |
| 37 correct_focus_script = """ | |
| 38 (function(listElem, itemElem, callback) { | |
| 39 if (document.activeElement == itemElem) { | |
| 40 callback(); | |
| 41 return; | |
| 42 } | |
| 43 itemElem.focus(); | |
| 44 listElem.focus(); | |
| 45 itemElem.addEventListener("focus", callback); | |
| 46 }).apply(null, arguments); | |
| 47 """ | |
| 48 driver.set_script_timeout(5) | |
| 49 try: | |
| 50 driver.execute_async_script(correct_focus_script, list_elem, field_elem) | |
| 51 except selenium.common.exceptions.TimeoutException: | |
| 52 raise RuntimeError('Unable to focus list item ' + field_elem.tag_name) | |
| 53 | |
| 54 | |
| 55 class Item(object): | |
| 56 """A list item web element.""" | |
| 57 def __init__(self, elem): | |
| 58 self._elem = elem | |
| 59 | |
| 60 def Remove(self, driver): | |
| 61 button = self._elem.find_element_by_xpath('./button') | |
| 62 ActionChains(driver).move_to_element(button).click().perform() | |
| 63 | |
| 64 | |
| 65 class TextFieldsItem(Item): | |
| 66 """An item consisting only of text fields.""" | |
| 67 def _GetFields(self): | |
| 68 """Returns the text fields list.""" | |
| 69 return self._elem.find_elements_by_tag_name('input') | |
| 70 | |
| 71 def Set(self, values): | |
| 72 """Sets the value(s) of the item's text field(s). | |
| 73 | |
| 74 Args: | |
| 75 values: The new value or the list of the new values of the fields. | |
| 76 """ | |
| 77 field_list = self._GetFields() | |
| 78 if len(field_list) > 1: | |
| 79 assert type(values) == types.ListType, \ | |
| 80 """The values must be a list for a HTML list that has multi-field | |
| 81 items. '%s' should be in a list.""" % values | |
| 82 value_list = values | |
| 83 else: | |
| 84 value_list = [values] | |
| 85 | |
| 86 assert len(field_list) == len(value_list), \ | |
| 87 """The item to be added must have the same number of fields as an item | |
| 88 in the HTML list. Given item '%s' should have %s fields.""" % ( | |
| 89 value_list, len(field_list)) | |
| 90 for field, value in zip(field_list, value_list): | |
| 91 field.clear() | |
| 92 field.send_keys(value) | |
| 93 field_list[-1].send_keys('\n') # press enter on the last field. | |
| 94 | |
| 95 def Get(self): | |
| 96 """Returns the list of the text field values.""" | |
| 97 return map(lambda f: f.get_attribute('value'), self._GetFields()) | |
| 98 | |
| 99 | |
| 100 class TextField(object): | |
| 101 """A text field web element.""" | |
| 102 def __init__(self, elem): | |
| 103 self._elem = elem | |
| 104 | |
| 105 def Set(self, value): | |
| 106 """Sets the value of the text field. | |
| 107 | |
| 108 Args: | |
| 109 value: The new value of the field. | |
| 110 """ | |
| 111 self._elem.clear() | |
| 112 self._elem.send_keys(value) | |
| 113 | |
| 114 def Get(self): | |
| 115 """Returns the value of the text field.""" | |
| 116 return self._elem.get_attribute('value') | |
| 117 | |
| 118 | |
| 119 class List(object): | |
| 120 """A web element that holds a list of items.""" | |
| 121 | |
| 122 def __init__(self, driver, elem, item_class=Item): | |
| 123 """item element is an element in the HTML list. | |
| 124 item class is the class of item the list holds.""" | |
| 125 self._driver = driver | |
| 126 self._elem = elem | |
| 127 self._item_class = item_class | |
| 128 | |
| 129 def RemoveAll(self): | |
| 130 """Removes all items from the list. | |
| 131 | |
| 132 In the loop the removal of an elem renders the remaining elems of the list | |
| 133 invalid. After each item is removed, GetItems() is called. | |
| 134 """ | |
| 135 for i in range(len(self.GetItems())): | |
| 136 self.GetItems()[0].Remove(self._driver) | |
| 137 | |
| 138 def GetItems(self): | |
| 139 """Returns all the items that are in the list.""" | |
| 140 items = self._GetItemElems() | |
| 141 return map(lambda x: self._item_class(x), items) | |
| 142 | |
| 143 def GetSize(self): | |
| 144 """Returns the number of items in the list.""" | |
| 145 return len(self._GetItemElems()) | |
| 146 | |
| 147 def _GetItemElems(self): | |
| 148 return self._elem.find_elements_by_xpath('.//*[@role="listitem"]') | |
| 149 | |
| 150 | |
| 151 class DynamicList(List): | |
| 152 """A web element that holds a dynamic list of items of text fields. | |
| 153 | |
| 154 Terminology: | |
| 155 item element: an element in the HTML list item. | |
| 156 item_class: the class of item the list holds | |
| 157 placeholder: the last item element in the list, which is not committed yet | |
| 158 | |
| 159 The user can add new items to the list by typing in the placeholder item. | |
| 160 When a user presses enter or focuses something else, the placeholder item | |
| 161 is committed and a new placeholder is created. An item may contain 1 or | |
| 162 more text fields. | |
| 163 """ | |
| 164 | |
| 165 def __init__(self, driver, elem, item_class=TextFieldsItem): | |
| 166 return super(DynamicList, self).__init__( | |
| 167 driver, elem, item_class=item_class) | |
| 168 | |
| 169 def GetPlaceholderItem(self): | |
| 170 return self.GetItems()[-1] | |
| 171 | |
| 172 def GetCommittedItems(self): | |
| 173 """Returns all the items that are in the list, except the placeholder.""" | |
| 174 return map(lambda x: self._item_class(x), self._GetCommittedItemElems()) | |
| 175 | |
| 176 def GetSize(self): | |
| 177 """Returns the number of items in the list, excluding the placeholder.""" | |
| 178 return len(self._GetCommittedItemElems()) | |
| 179 | |
| 180 def _GetCommittedItemElems(self): | |
| 181 return self._GetItemElems()[:-1] | |
| 182 | |
| 183 def _GetPlaceholderElem(self): | |
| 184 return self._GetItemElems()[-1] | |
| 185 | |
| 186 | |
| 187 class AutofillEditAddressDialog(object): | |
| 188 """The overlay for editing an autofill address.""" | |
| 189 | |
| 190 _URL = 'chrome://settings-frame/autofillEditAddress' | |
| 191 | |
| 192 @staticmethod | |
| 193 def FromNavigation(driver): | |
| 194 """Creates an instance of the dialog by navigating directly to it.""" | |
| 195 driver.get(AutofillEditAddressDialog._URL) | |
| 196 return AutofillEditAddressDialog(driver) | |
| 197 | |
| 198 def __init__(self, driver): | |
| 199 self.driver = driver | |
| 200 assert self._URL == driver.current_url | |
| 201 self.dialog_elem = driver.find_element_by_id( | |
| 202 'autofill-edit-address-overlay') | |
| 203 | |
| 204 def Fill(self, names=None, addr_line_1=None, city=None, state=None, | |
| 205 postal_code=None, country_code=None, phones=None): | |
| 206 """Fills in the given data into the appropriate fields. | |
| 207 | |
| 208 If filling into a text field, the given value will replace the current one. | |
| 209 If filling into a list, the values will be added after all items are | |
| 210 deleted. | |
| 211 | |
| 212 Note: 'names', in the new autofill UI, is an array of full names. A full | |
| 213 name is an array of first, middle, last names. Example: | |
| 214 names=[['Joe', '', 'King'], ['Fred', 'W', 'Michno']] | |
| 215 | |
| 216 Args: | |
| 217 names: List of names; each name should be [first, middle, last]. | |
| 218 addr_line_1: First line in the address. | |
| 219 city: City. | |
| 220 state: State. | |
| 221 postal_code: Postal code (zip code for US). | |
| 222 country_code: Country code (e.g., US or FR). | |
| 223 phones: List of phone numbers. | |
| 224 """ | |
| 225 id_dict = {'addr-line-1': addr_line_1, | |
| 226 'city': city, | |
| 227 'state': state, | |
| 228 'postal-code': postal_code} | |
| 229 for id, value in id_dict.items(): | |
| 230 if value is not None: | |
| 231 TextField(self.dialog_elem.find_element_by_id(id)).Set(value) | |
| 232 | |
| 233 list_id_dict = {'full-name-list': names, | |
| 234 'phone-list': phones} | |
| 235 for list_id, values in list_id_dict.items(): | |
| 236 if values is not None: | |
| 237 list = DynamicList(self.driver, | |
| 238 self.dialog_elem.find_element_by_id(list_id)) | |
| 239 list.RemoveAll() | |
| 240 for value in values: | |
| 241 list.GetPlaceholderItem().Set(value) | |
| 242 | |
| 243 if country_code is not None: | |
| 244 self.dialog_elem.find_element_by_xpath( | |
| 245 './/*[@id="country"]/*[@value="%s"]' % country_code).click() | |
| 246 | |
| 247 def GetStateLabel(self): | |
| 248 """Returns the label used for the state text field.""" | |
| 249 return self.dialog_elem.find_element_by_id('state-label').text | |
| 250 | |
| 251 def GetPostalCodeLabel(self): | |
| 252 """Returns the label used for the postal code text field.""" | |
| 253 return self.dialog_elem.find_element_by_id('postal-code-label').text | |
| 254 | |
| 255 def GetPhones(self): | |
| 256 """Returns a list of the phone numbers in the phones list.""" | |
| 257 list = DynamicList( | |
| 258 self.driver, self.dialog_elem.find_element_by_id('phone-list')) | |
| 259 return [item.Get()[0] for item in list.GetCommittedItems()] | |
| 260 | |
| 261 | |
| 262 class ContentTypes(object): | |
| 263 COOKIES = 'cookies' | |
| 264 IMAGES = 'images' | |
| 265 JAVASCRIPT = 'javascript' | |
| 266 HANDLERS = 'handlers' | |
| 267 PLUGINS = 'plugins' | |
| 268 POPUPS = 'popups' | |
| 269 GEOLOCATION = 'location' | |
| 270 NOTIFICATIONS = 'notifications' | |
| 271 PASSWORDS = 'passwords' | |
| 272 | |
| 273 | |
| 274 class Behaviors(object): | |
| 275 ALLOW = 'allow' | |
| 276 SESSION_ONLY = 'session_only' | |
| 277 ASK = 'ask' | |
| 278 BLOCK = 'block' | |
| 279 | |
| 280 | |
| 281 class ContentSettingsPage(object): | |
| 282 """The overlay for managing exceptions on the Content Settings page.""" | |
| 283 | |
| 284 _URL = 'chrome://settings-frame/content' | |
| 285 | |
| 286 @staticmethod | |
| 287 def FromNavigation(driver): | |
| 288 """Creates an instance of the dialog by navigating directly to it.""" | |
| 289 driver.get(ContentSettingsPage._URL) | |
| 290 return ContentSettingsPage(driver) | |
| 291 | |
| 292 def __init__(self, driver): | |
| 293 assert self._URL == driver.current_url | |
| 294 self.page_elem = driver.find_element_by_id( | |
| 295 'content-settings-page') | |
| 296 | |
| 297 def SetContentTypeOption(self, content_type, option): | |
| 298 """Set the option for the specified content type. | |
| 299 | |
| 300 Args: | |
| 301 content_type: The content type to manage. | |
| 302 option: The option to allow, deny or ask. | |
| 303 """ | |
| 304 self.page_elem.find_element_by_xpath( | |
| 305 './/*[@name="%s"][@value="%s"]' % (content_type, option)).click() | |
| 306 | |
| 307 | |
| 308 class ManageExceptionsPage(object): | |
| 309 """The overlay for the content exceptions page.""" | |
| 310 | |
| 311 @staticmethod | |
| 312 def FromNavigation(driver, content_type): | |
| 313 """Creates an instance of the dialog by navigating directly to it. | |
| 314 | |
| 315 Args: | |
| 316 driver: The remote WebDriver instance to manage some content type. | |
| 317 content_type: The content type to manage. | |
| 318 """ | |
| 319 content_url = 'chrome://settings-frame/contentExceptions#%s' % content_type | |
| 320 driver.get(content_url) | |
| 321 return ManageExceptionsPage(driver, content_type) | |
| 322 | |
| 323 def __init__(self, driver, content_type): | |
| 324 self._list_elem = driver.find_element_by_xpath( | |
| 325 './/*[@id="content-settings-exceptions-area"]' | |
| 326 '//*[@contenttype="%s"]//list[@role="list"]' | |
| 327 '[@class="settings-list"]' % content_type) | |
| 328 self._driver = driver | |
| 329 self._content_type = content_type | |
| 330 try: | |
| 331 self._incognito_list_elem = driver.find_element_by_xpath( | |
| 332 './/*[@id="content-settings-exceptions-area"]' | |
| 333 '//*[@contenttype="%s"]//div[not(@hidden)]' | |
| 334 '//list[@mode="otr"][@role="list"]' | |
| 335 '[@class="settings-list"]' % content_type) | |
| 336 except selenium.common.exceptions.NoSuchElementException: | |
| 337 self._incognito_list_elem = None | |
| 338 | |
| 339 def _AssertIncognitoAvailable(self): | |
| 340 if not self._incognito_list_elem: | |
| 341 raise AssertionError( | |
| 342 'Incognito settings in "%s" content page not available' | |
| 343 % self._content_type) | |
| 344 | |
| 345 def _GetExceptionList(self, incognito): | |
| 346 if not incognito: | |
| 347 list_elem = self._list_elem | |
| 348 else: | |
| 349 list_elem = self._incognito_list_elem | |
| 350 return DynamicList(self._driver, list_elem) | |
| 351 | |
| 352 def _GetPatternList(self, incognito): | |
| 353 if not incognito: | |
| 354 list_elem = self._list_elem | |
| 355 else: | |
| 356 list_elem = self._incognito_list_elem | |
| 357 pattern_list = [p.text for p in | |
| 358 list_elem.find_elements_by_xpath( | |
| 359 './/*[contains(@class, "exception-pattern")]' | |
| 360 '//*[@class="static-text"]')] | |
| 361 return pattern_list | |
| 362 | |
| 363 def AddNewException(self, pattern, behavior, incognito=False): | |
| 364 """Add a new pattern and behavior to the Exceptions page. | |
| 365 | |
| 366 Args: | |
| 367 pattern: Hostname pattern string. | |
| 368 behavior: Setting for the hostname pattern (Allow, Block, Session Only). | |
| 369 incognito: Incognito list box. Display to false. | |
| 370 | |
| 371 Raises: | |
| 372 AssertionError when an exception cannot be added on the content page. | |
| 373 """ | |
| 374 if incognito: | |
| 375 self._AssertIncognitoAvailable() | |
| 376 list_elem = self._incognito_list_elem | |
| 377 else: | |
| 378 list_elem = self._list_elem | |
| 379 # Select behavior first. | |
| 380 try: | |
| 381 list_elem.find_element_by_xpath( | |
| 382 './/*[@class="exception-setting"]' | |
| 383 '[not(@displaymode)]//option[@value="%s"]' | |
| 384 % behavior).click() | |
| 385 except selenium.common.exceptions.NoSuchElementException: | |
| 386 raise AssertionError( | |
| 387 'Adding new exception not allowed in "%s" content page' | |
| 388 % self._content_type) | |
| 389 # Set pattern now. | |
| 390 self._GetExceptionList(incognito).GetPlaceholderItem().Set(pattern) | |
| 391 | |
| 392 def DeleteException(self, pattern, incognito=False): | |
| 393 """Delete the exception for the selected hostname pattern. | |
| 394 | |
| 395 Args: | |
| 396 pattern: Hostname pattern string. | |
| 397 incognito: Incognito list box. Default to false. | |
| 398 """ | |
| 399 if incognito: | |
| 400 self._AssertIncognitoAvailable() | |
| 401 list = self._GetExceptionList(incognito) | |
| 402 items = filter(lambda item: item.Get()[0] == pattern, | |
| 403 list.GetComittedItems()) | |
| 404 map(lambda item: item.Remove(self._driver), items) | |
| 405 | |
| 406 def GetExceptions(self, incognito=False): | |
| 407 """Returns a dictionary of {pattern: behavior}. | |
| 408 | |
| 409 Example: {'file:///*': 'block'} | |
| 410 | |
| 411 Args: | |
| 412 incognito: Incognito list box. Default to false. | |
| 413 """ | |
| 414 if incognito: | |
| 415 self._AssertIncognitoAvailable() | |
| 416 list_elem = self._incognito_list_elem | |
| 417 else: | |
| 418 list_elem = self._list_elem | |
| 419 pattern_list = self._GetPatternList(incognito) | |
| 420 behavior_list = list_elem.find_elements_by_xpath( | |
| 421 './/*[@role="listitem"][@class="deletable-item"]' | |
| 422 '//*[@class="exception-setting"][@displaymode="static"]') | |
| 423 assert len(pattern_list) == len(behavior_list), \ | |
| 424 'Number of patterns does not match the behaviors.' | |
| 425 return dict(zip(pattern_list, [b.text.lower() for b in behavior_list])) | |
| 426 | |
| 427 def GetBehaviorForPattern(self, pattern, incognito=False): | |
| 428 """Returns the behavior for a given pattern on the Exceptions page. | |
| 429 | |
| 430 Args: | |
| 431 pattern: Hostname pattern string. | |
| 432 incognito: Incognito list box. Default to false. | |
| 433 """ | |
| 434 if incognito: | |
| 435 self._AssertIncognitoAvailable() | |
| 436 assert self.GetExceptions(incognito).has_key(pattern), \ | |
| 437 'No displayed host name matches pattern "%s"' % pattern | |
| 438 return self.GetExceptions(incognito)[pattern] | |
| 439 | |
| 440 def SetBehaviorForPattern(self, pattern, behavior, incognito=False): | |
| 441 """Set the behavior for the selected pattern on the Exceptions page. | |
| 442 | |
| 443 Args: | |
| 444 pattern: Hostname pattern string. | |
| 445 behavior: Setting for the hostname pattern (Allow, Block, Session Only). | |
| 446 incognito: Incognito list box. Default to false. | |
| 447 | |
| 448 Raises: | |
| 449 AssertionError when the behavior cannot be changed on the content page. | |
| 450 """ | |
| 451 if incognito: | |
| 452 self._AssertIncognitoAvailable() | |
| 453 list_elem = self._incognito_list_elem | |
| 454 else: | |
| 455 list_elem = self._list_elem | |
| 456 pattern_list = self._GetPatternList(incognito) | |
| 457 listitem_list = list_elem.find_elements_by_xpath( | |
| 458 './/*[@role="listitem"][@class="deletable-item"]') | |
| 459 pattern_listitem_dict = dict(zip(pattern_list, listitem_list)) | |
| 460 # Set focus to appropriate listitem. | |
| 461 listitem_elem = pattern_listitem_dict[pattern] | |
| 462 listitem_elem.click() | |
| 463 # Set behavior. | |
| 464 try: | |
| 465 listitem_elem.find_element_by_xpath( | |
| 466 './/option[@value="%s"]' % behavior).click() | |
| 467 except selenium.common.exceptions.ElementNotVisibleException: | |
| 468 raise AssertionError( | |
| 469 'Changing the behavior is invalid for pattern ' | |
| 470 '"%s" in "%s" content page' % (behavior, self._content_type)) | |
| 471 # Send enter key. | |
| 472 pattern_elem = listitem_elem.find_element_by_tag_name('input') | |
| 473 pattern_elem.send_keys('\n') | |
| 474 | |
| 475 | |
| 476 class RestoreOnStartupType(object): | |
| 477 NEW_TAB_PAGE = 5 | |
| 478 RESTORE_SESSION = 1 | |
| 479 RESTORE_URLS = 4 | |
| 480 | |
| 481 | |
| 482 class BasicSettingsPage(object): | |
| 483 """The basic settings page.""" | |
| 484 _URL = 'chrome://settings-frame/settings' | |
| 485 | |
| 486 @staticmethod | |
| 487 def FromNavigation(driver): | |
| 488 """Creates an instance of BasicSetting page by navigating to it.""" | |
| 489 driver.get(BasicSettingsPage._URL) | |
| 490 return BasicSettingsPage(driver) | |
| 491 | |
| 492 def __init__(self, driver): | |
| 493 self._driver = driver | |
| 494 assert self._URL == driver.current_url | |
| 495 | |
| 496 def SetOnStartupOptions(self, on_startup_option): | |
| 497 """Set on-startup options. | |
| 498 | |
| 499 Args: | |
| 500 on_startup_option: option types for on start up settings. | |
| 501 | |
| 502 Raises: | |
| 503 AssertionError when invalid startup option type is provided. | |
| 504 """ | |
| 505 if on_startup_option == RestoreOnStartupType.NEW_TAB_PAGE: | |
| 506 startup_option_elem = self._driver.find_element_by_id('startup-newtab') | |
| 507 elif on_startup_option == RestoreOnStartupType.RESTORE_SESSION: | |
| 508 startup_option_elem = self._driver.find_element_by_id( | |
| 509 'startup-restore-session') | |
| 510 elif on_startup_option == RestoreOnStartupType.RESTORE_URLS: | |
| 511 startup_option_elem = self._driver.find_element_by_id( | |
| 512 'startup-show-pages') | |
| 513 else: | |
| 514 raise AssertionError('Invalid value for restore start up option!') | |
| 515 startup_option_elem.click() | |
| 516 | |
| 517 def _GoToStartupSetPages(self): | |
| 518 self._driver.find_element_by_id('startup-set-pages').click() | |
| 519 | |
| 520 def _FillStartupURL(self, url): | |
| 521 list = DynamicList(self._driver, self._driver.find_element_by_id( | |
| 522 'startupPagesList')) | |
| 523 list.GetPlaceholderItem().Set(url + '\n') | |
| 524 | |
| 525 def AddStartupPage(self, url): | |
| 526 """Add a startup URL. | |
| 527 | |
| 528 Args: | |
| 529 url: A startup url. | |
| 530 """ | |
| 531 self._GoToStartupSetPages() | |
| 532 self._FillStartupURL(url) | |
| 533 self._driver.find_element_by_id('startup-overlay-confirm').click() | |
| 534 self._driver.get(self._URL) | |
| 535 | |
| 536 def UseCurrentPageForStartup(self, title_list): | |
| 537 """Use current pages and verify page url show up in settings. | |
| 538 | |
| 539 Args: | |
| 540 title_list: startup web page title list. | |
| 541 """ | |
| 542 self._GoToStartupSetPages() | |
| 543 self._driver.find_element_by_id('startupUseCurrentButton').click() | |
| 544 self._driver.find_element_by_id('startup-overlay-confirm').click() | |
| 545 def is_current_page_visible(driver): | |
| 546 title_elem_list = driver.find_elements_by_xpath( | |
| 547 '//*[contains(@class, "title")][text()="%s"]' % title_list[0]) | |
| 548 if len(title_elem_list) == 0: | |
| 549 return False | |
| 550 return True | |
| 551 WebDriverWait(self._driver, 10).until(is_current_page_visible) | |
| 552 self._driver.get(self._URL) | |
| 553 | |
| 554 def VerifyStartupURLs(self, title_list): | |
| 555 """Verify saved startup URLs appear in set page UI. | |
| 556 | |
| 557 Args: | |
| 558 title_list: A list of startup page title. | |
| 559 | |
| 560 Raises: | |
| 561 AssertionError when start up URLs do not appear in set page UI. | |
| 562 """ | |
| 563 self._GoToStartupSetPages() | |
| 564 for i in range(len(title_list)): | |
| 565 try: | |
| 566 self._driver.find_element_by_xpath( | |
| 567 '//*[contains(@class, "title")][text()="%s"]' % title_list[i]) | |
| 568 except selenium.common.exceptions.NoSuchElementException: | |
| 569 raise AssertionError("Current page %s did not appear as startup page." | |
| 570 % title_list[i]) | |
| 571 self._driver.find_element_by_id('startup-overlay-cancel').click() | |
| 572 | |
| 573 def CancelStartupURLSetting(self, url): | |
| 574 """Cancel start up URL settings. | |
| 575 | |
| 576 Args: | |
| 577 url: A startup url. | |
| 578 """ | |
| 579 self._GoToStartupSetPages() | |
| 580 self._FillStartupURL(url) | |
| 581 self._driver.find_element_by_id('startup-overlay-cancel').click() | |
| 582 self._driver.get(self._URL) | |
| 583 | |
| 584 | |
| 585 class PasswordsSettings(object): | |
| 586 """The overlay for managing passwords on the Content Settings page.""" | |
| 587 | |
| 588 _URL = 'chrome://settings-frame/passwords' | |
| 589 | |
| 590 class PasswordsItem(Item): | |
| 591 """A list of passwords item web element.""" | |
| 592 def _GetFields(self): | |
| 593 """Returns the field list element.""" | |
| 594 return self._elem.find_elements_by_xpath('./div/*') | |
| 595 | |
| 596 def GetSite(self): | |
| 597 """Returns the site field value.""" | |
| 598 return self._GetFields()[0].text | |
| 599 | |
| 600 def GetUsername(self): | |
| 601 """Returns the username field value.""" | |
| 602 return self._GetFields()[1].text | |
| 603 | |
| 604 | |
| 605 @staticmethod | |
| 606 def FromNavigation(driver): | |
| 607 """Creates an instance of the dialog by navigating directly to it. | |
| 608 | |
| 609 Args: | |
| 610 driver: The remote WebDriver instance to manage some content type. | |
| 611 """ | |
| 612 driver.get(PasswordsSettings._URL) | |
| 613 return PasswordsSettings(driver) | |
| 614 | |
| 615 def __init__(self, driver): | |
| 616 self._driver = driver | |
| 617 assert self._URL == driver.current_url | |
| 618 list_elem = driver.find_element_by_id('saved-passwords-list') | |
| 619 self._items_list = List(self._driver, list_elem, self.PasswordsItem) | |
| 620 | |
| 621 def DeleteItem(self, url, username): | |
| 622 """Deletes a line entry in Passwords Content Settings. | |
| 623 | |
| 624 Args: | |
| 625 url: The URL string as it appears in the UI. | |
| 626 username: The username string as it appears in the second column. | |
| 627 """ | |
| 628 for password_item in self._items_list.GetItems(): | |
| 629 if (password_item.GetSite() == url and | |
| 630 password_item.GetUsername() == username): | |
| 631 password_item.Remove(self._driver) | |
| 632 | |
| 633 | |
| 634 class CookiesAndSiteDataSettings(object): | |
| 635 """The overlay for managing cookies on the Content Settings page.""" | |
| 636 | |
| 637 _URL = 'chrome://settings-frame/cookies' | |
| 638 | |
| 639 @staticmethod | |
| 640 def FromNavigation(driver): | |
| 641 """Creates an instance of the dialog by navigating directly to it. | |
| 642 | |
| 643 Args: | |
| 644 driver: The remote WebDriver instance for managing content type. | |
| 645 """ | |
| 646 driver.get(CookiesAndSiteDataSettings._URL) | |
| 647 return CookiesAndSiteDataSettings(driver) | |
| 648 | |
| 649 def __init__(self, driver): | |
| 650 self._driver = driver | |
| 651 assert self._URL == driver.current_url | |
| 652 self._list_elem = driver.find_element_by_id('cookies-list') | |
| 653 | |
| 654 def GetSiteNameList(self): | |
| 655 """Returns a list of the site names. | |
| 656 | |
| 657 This is a public function since the test needs to check if the site is | |
| 658 deleted. | |
| 659 """ | |
| 660 site_list = [p.text for p in | |
| 661 self._list_elem.find_elements_by_xpath( | |
| 662 './/*[contains(@class, "deletable-item")]' | |
| 663 '//div[@class="cookie-site"]')] | |
| 664 return site_list | |
| 665 | |
| 666 def _GetCookieNameList(self): | |
| 667 """Returns a list where each item is the list of cookie names of each site. | |
| 668 | |
| 669 Example: site1 | cookie1 cookie2 | |
| 670 site2 | cookieA | |
| 671 site3 | cookieA cookie1 cookieB | |
| 672 | |
| 673 Returns: | |
| 674 A cookie names list such as: | |
| 675 [ ['cookie1', 'cookie2'], ['cookieA'], ['cookieA', 'cookie1', 'cookieB'] ] | |
| 676 """ | |
| 677 cookie_name_list = [] | |
| 678 for elem in self._list_elem.find_elements_by_xpath( | |
| 679 './/*[@role="listitem"]'): | |
| 680 elem.click() | |
| 681 cookie_name_list.append([c.text for c in | |
| 682 elem.find_elements_by_xpath('.//div[@class="cookie-item"]')]) | |
| 683 return cookie_name_list | |
| 684 | |
| 685 def DeleteSiteData(self, site): | |
| 686 """Delete a site entry with its cookies in cookies content settings. | |
| 687 | |
| 688 Args: | |
| 689 site: The site string as it appears in the UI. | |
| 690 """ | |
| 691 delete_button_list = self._list_elem.find_elements_by_class_name( | |
| 692 'row-delete-button') | |
| 693 site_list = self.GetSiteNameList() | |
| 694 for i in range(len(site_list)): | |
| 695 if site_list[i] == site: | |
| 696 # Highlight the item so the close button shows up, then delete button | |
| 697 # shows up, then click on the delete button. | |
| 698 ActionChains(self._driver).move_to_element( | |
| 699 delete_button_list[i]).click().perform() | |
| OLD | NEW |