Home | History | Annotate | Download | only in ap_configurators
      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 time
      6 
      7 from selenium.common.exceptions import NoSuchElementException
      8 from selenium.common.exceptions import TimeoutException as \
      9     SeleniumTimeoutException
     10 from selenium.common.exceptions import WebDriverException
     11 from selenium.webdriver.support.ui import WebDriverWait
     12 
     13 class WebDriverCoreHelpers(object):
     14     """Base class for manipulating web pages using webdriver."""
     15 
     16     def __init__(self):
     17         super(WebDriverCoreHelpers, self).__init__()
     18         self.driver = None
     19         self.wait = WebDriverWait(self.driver, timeout=5)
     20 
     21 
     22     def _check_for_alert_in_message(self, message, alert_handler):
     23         """Check for an alert in error message and handle it.
     24 
     25         @param message: The error message.
     26         @param alert_handler: The handler method to call.
     27 
     28         """
     29         if (message.find('An open modal dialog blocked') != -1 and
     30             message.find('unexpected alert open') != -1):
     31             alert = self.driver.switch_to_alert()
     32             alert_handler(alert)
     33         else:
     34             raise RuntimeError(message)
     35 
     36 
     37     def _handle_alert(self, xpath, alert_handler):
     38         """Calls the alert handler if there is an alert.
     39 
     40         @param xpath: The xpath that could raise the alert.
     41         @param alert_handler: the handler method to call.
     42 
     43         """
     44         try:
     45             self.driver.find_element_by_xpath(xpath)
     46             return
     47         except WebDriverException, e:
     48             message = str(e)
     49             # The messages differ based on the webdriver version
     50             if (message.find('An open modal dialog blocked') == -1 and
     51                 message.find('unexpected alert open') == -1):
     52                 return
     53             self._handler(alert_handler)
     54         # Sometimes routers put out multiple alert statements on the same page.
     55         self._handle_alert(xpath, alert_handler)
     56 
     57 
     58     def _handler(self, alert_handler):
     59         """Handles the alert.
     60 
     61         @param alert_handler: The custom handler method to call.
     62 
     63         """
     64         alert = self.driver.switch_to_alert()
     65         if not alert_handler:
     66             # The caller did not provide us with a handler, dismiss and raise.
     67             try:
     68                 alert_text = alert.text
     69             except WebDriverException:
     70                 # There is a bug in selenium where the alert object will exist
     71                 # but you can't get to the text object right away.
     72                 time.sleep(1)
     73             alert_text = alert.text
     74             alert.accept()
     75             raise RuntimeError('An alert was encountered and no handler was '
     76                                'specified.  The text from the alert was: %s'
     77                                % alert_text)
     78         alert_handler(alert)
     79 
     80 
     81     def set_wait_time(self, time):
     82         """Sets the wait time of webdriver commands.
     83 
     84         @param time: the time to wait in seconds.
     85 
     86         """
     87         self.wait = WebDriverWait(self.driver, timeout=time)
     88 
     89 
     90     def restore_default_wait_time(self):
     91         """Restores the default webdriver wait time."""
     92         self.wait = WebDriverWait(self.driver, timeout=5)
     93 
     94 
     95     def wait_for_objects_by_id(self, element_ids, wait_time=5):
     96         """Wait for one of the element_ids to show up.
     97 
     98         @param element_ids: A list of all the element ids to find.
     99         @param wait_time: The time to wait before giving up.
    100 
    101         @return The id that was found first.
    102 
    103         """
    104         xpaths = []
    105         for element_id in element_ids:
    106             xpaths.append('id("%s")' % element_id)
    107         xpath_found = self.wait_for_objects_by_xpath(xpaths, wait_time)
    108         for element_id in element_ids:
    109             if element_id in xpath_found:
    110                 return element_id
    111 
    112 
    113     def wait_for_objects_by_xpath(self, xpaths, wait_time=5):
    114         """Wait for one of the items in the xpath to show up.
    115 
    116         @param xpaths: A list of all the xpath's of elements to find.
    117         @param wait_time: The time to wait before giving up.
    118 
    119         @return The xpath that was found first.
    120 
    121         """
    122         excpetion = None
    123         if wait_time < len(xpaths):
    124             wait_time = len(xpaths)
    125         start_time = int(time.time())
    126         while (int(time.time()) - start_time) < wait_time:
    127             for xpath in xpaths:
    128                 try:
    129                     element = self.wait_for_object_by_xpath(xpath,
    130                                                             wait_time=0.25)
    131                     if element and element.is_displayed():
    132                         return xpath
    133                 except SeleniumTimeoutException, e:
    134                     exception = str(e)
    135                     pass
    136         raise SeleniumTimeoutException(exception)
    137 
    138 
    139     def click_button_by_id(self, element_id, alert_handler=None):
    140         """Clicks a button by id.
    141 
    142         @param element_id: the id of the button
    143         @param alert_handler: method invoked if an alert is detected. The method
    144                               must take one parameter, a webdriver alert object
    145 
    146         """
    147         xpath = 'id("%s")' % element_id
    148         return self.click_button_by_xpath(xpath, alert_handler)
    149 
    150 
    151     def click_button_by_xpath(self, xpath, alert_handler=None):
    152         """Clicks a button by xpath.
    153 
    154         @param xpath: the xpath of the button
    155         @param alert_handler: method invoked if an alert is detected. The method
    156                               must take one parameter, a webdriver alert object
    157 
    158         """
    159         button = self.wait_for_object_by_xpath(xpath)
    160         button.click()
    161         self._handle_alert(xpath, alert_handler)
    162 
    163 
    164     def get_url(self, page_url, page_title=None, element_xpath=None):
    165         """Load page and check if the page loads completely, if not, reload.
    166 
    167         @param page_url: The url to load.
    168         @param page_title: The complete/partial title of the page after loaded.
    169         @param element_xpath: The element that we search for to confirm that
    170                               the page loaded.
    171 
    172         """
    173         self.driver.get(page_url)
    174         if page_title:
    175             try:
    176                 self.wait.until(lambda _: page_title in self.driver.title)
    177             except SeleniumTimeoutException, e:
    178                 self.driver.get(page_url)
    179                 self.wait.until(lambda _: self.driver.title)
    180             finally:
    181                 if not page_title in self.driver.title:
    182                     raise WebDriverException('Page did not load. Expected %s in'
    183                                              'title, but got %s as title.' %
    184                                              (page_title, self.driver.title))
    185         if element_xpath:
    186             self.wait_for_object_by_xpath(element_xpath)
    187 
    188 
    189     def wait_for_object_by_id(self, element_id, wait_time=5):
    190         """Waits for an element to become available; returns a reference to it.
    191 
    192         @param element_id: the id of the element to wait for
    193         @param wait_time: the time to wait for the object
    194 
    195         @returns a reference to the element if found before a timeout.
    196 
    197         """
    198         xpath = 'id("%s")' % element_id
    199         return self.wait_for_object_by_xpath(xpath, wait_time=wait_time)
    200 
    201 
    202     def wait_for_object_by_xpath_to_vanish(self, xpath, wait_time=5):
    203         """Wait for the item in xpath to disappear from page.
    204 
    205         @param xpath: The xpath of the object to wait on.
    206         @param wait_time: The time to wait before giving up.
    207 
    208         @return void or raise exception if object does not vanish.
    209 
    210         """
    211         start_time = int(time.time())
    212         while (int(time.time()) - start_time) < wait_time:
    213             if self.object_by_xpath_exist(xpath):
    214                 time.sleep(0.5)
    215             else:
    216                 return
    217         raise SeleniumTimeoutException('The object with xpath %s failed to'
    218                                        ' vanish.' % xpath)
    219 
    220 
    221     def wait_for_object_by_id_to_vanish(self, element_id, wait_time=5):
    222         """Wait for the item in xpath to disappear from page.
    223 
    224         @param element_id: The id of the object to wait on.
    225         @param wait_time: The time to wait before giving up.
    226 
    227         @return void or raise exception if object does not vanish.
    228 
    229         """
    230         xpath = 'id("%s")' % element_id
    231         return self.wait_for_object_by_xpath_to_vanish(xpath,
    232                                                        wait_time=wait_time)
    233 
    234 
    235     def object_by_id_exist(self, element_id):
    236         """Finds if an object exist in this particular page.
    237 
    238         @param element_id: the id of the element to find
    239 
    240         @returns True if the element exists. False if the element does not.
    241 
    242         """
    243         xpath = 'id("%s")' % element_id
    244         return self.object_by_xpath_exist(xpath)
    245 
    246 
    247     def object_by_xpath_exist(self, xpath):
    248         """Finds if an object exist in this particular page.
    249 
    250         @param xpath: the xpath of the element to find
    251 
    252         @returns True if the xpath exists. False if the xpath does not.
    253 
    254         """
    255         try:
    256             self.wait_for_object_by_xpath(xpath)
    257         except SeleniumTimeoutException:
    258             return False
    259         return True
    260 
    261 
    262     def wait_for_object_by_xpath(self, xpath, wait_time=5):
    263         """Waits for an element to become available; returns a reference to it.
    264 
    265         @param xpath: the xpath of the element to wait for
    266         @param wait_time: the time to wait for the object.
    267 
    268         @returns reference to the element if found before a timeout.
    269 
    270         """
    271         self.set_wait_time(wait_time)
    272         try:
    273             self.wait.until(lambda _: self.driver.find_element_by_xpath(xpath))
    274             element = self.driver.find_element_by_xpath(xpath)
    275         except (SeleniumTimeoutException, NoSuchElementException) as e:
    276             self.restore_default_wait_time()
    277             raise SeleniumTimeoutException('Unable to find the object by '
    278                                            'xpath: %s\n WebDriver exception: '
    279                                            '%s' % (xpath, str(e)))
    280         self.restore_default_wait_time()
    281         return element
    282 
    283 
    284     def item_in_popup_by_id_exist(self, item, element_id):
    285         """Returns if an item exists in a popup given a id
    286 
    287         @param item: name of the item
    288         @param element_id: the id of the popup
    289 
    290         @returns True if the item exists; False otherwise.
    291 
    292         """
    293         xpath = 'id("%s")' % element_id
    294         return self.item_in_popup_by_xpath_exist(item, xpath)
    295 
    296 
    297     def item_in_popup_by_xpath_exist(self, item, xpath):
    298         """Returns if an item exists in a popup given an xpath
    299 
    300         @param item: name of the item
    301         @param xpath: the xpath of the popup
    302 
    303         @returns True if the item exists; False otherwise.
    304 
    305         """
    306         if self.number_of_items_in_popup_by_xpath(xpath) == 0:
    307             raise SeleniumTimeoutException('The popup at xpath %s has no items.'
    308                                            % xpath)
    309         popup = self.driver.find_element_by_xpath(xpath)
    310         for option in popup.find_elements_by_tag_name('option'):
    311             if option.text == item:
    312                 return True
    313         return False
    314 
    315 
    316     def number_of_items_in_popup_by_id(self, element_id, alert_handler=None):
    317         """Returns the number of items in a popup given the element ID.
    318 
    319         @param element_id: the html ID of the item
    320         @param alert_handler: method invoked if an alert is detected. The method
    321                               must take one parameter, a webdriver alert object
    322 
    323         @returns the number of items in the popup.
    324 
    325         """
    326         xpath = 'id("%s")' % element_id
    327         return self.number_of_items_in_popup_by_xpath(xpath, alert_handler)
    328 
    329 
    330     def number_of_items_in_popup_by_xpath(self, xpath, alert_handler=None):
    331         """Returns the number of items in a popup given a xpath
    332 
    333         @param xpath: the xpath of the popup
    334         @param alert_handler: method invoked if an alert is detected. The method
    335                          must take one parameter, a webdriver alert object
    336 
    337         @returns the number of items in the popup.
    338 
    339         """
    340         popup = self.driver.find_element_by_xpath(xpath)
    341         try:
    342             self.wait.until(lambda _:
    343                             len(popup.find_elements_by_tag_name('option')))
    344         except SeleniumTimeoutException, e:
    345             return 0
    346         return len(popup.find_elements_by_tag_name('option'))
    347 
    348 
    349     def select_item_from_popup_by_id(self, item, element_id,
    350                                      wait_for_xpath=None, alert_handler=None):
    351         """Selects an item from a popup, by passing the element ID.
    352 
    353         @param item: the string of the item to select from the popup
    354         @param element_id: the html ID of the item
    355         @param wait_for_xpath: an item to wait for before returning, if not
    356                                specified the method does not wait.
    357         @param alert_handler: method invoked if an alert is detected. The method
    358                               must take one parameter, a webdriver alert object
    359 
    360         """
    361         xpath = 'id("%s")' % element_id
    362         self.select_item_from_popup_by_xpath(item, xpath, wait_for_xpath,
    363                                              alert_handler)
    364 
    365 
    366     def select_item_from_popup_by_xpath(self, item, xpath, wait_for_xpath=None,
    367                                         alert_handler=None):
    368         """Selects an item from a popup, by passing the xpath of the popup.
    369 
    370         @param item: the string of the item to select from the popup
    371         @param xpath: the xpath of the popup
    372         @param wait_for_xpath: an item to wait for before returning, if not
    373                                specified the method does not wait.
    374         @param alert_handler: method invoked if an alert is detected. The method
    375                               must take one parameter, a webdriver alert object
    376 
    377         """
    378         if self.number_of_items_in_popup_by_xpath(xpath) == 0:
    379             raise SeleniumTimeoutException('The popup at xpath %s has no items.'
    380                                            % xpath)
    381         if not self.item_in_popup_by_xpath_exist(item, xpath):
    382             raise SeleniumTimeoutException('The popup at xpath %s does not '
    383                                            'contain the item %s.' % (xpath,
    384                                            item))
    385         popup = self.driver.find_element_by_xpath(xpath)
    386         for option in popup.find_elements_by_tag_name('option'):
    387             if option.text == item:
    388                 option.click()
    389                 break
    390         self._handle_alert(xpath, alert_handler)
    391         if wait_for_xpath:
    392             self.wait_for_object_by_xpath(wait_for_xpath)
    393 
    394 
    395     def set_content_of_text_field_by_id(self, content, text_field_id,
    396                                         wait_for_xpath=None,
    397                                         abort_check=False):
    398         """Sets the content of a textfield, by passing the element ID.
    399 
    400         @param content: the content to apply to the textfield
    401         @param text_field_id: the html ID of the textfield
    402         @param wait_for_xpath: an item to wait for before returning, if not
    403                                specified the method does not wait.
    404 
    405         """
    406         xpath = 'id("%s")' % text_field_id
    407         self.set_content_of_text_field_by_xpath(content, xpath,
    408                                                 wait_for_xpath=wait_for_xpath,
    409                                                 abort_check=abort_check)
    410 
    411 
    412     def set_content_of_text_field_by_xpath(self, content, xpath,
    413                                            wait_for_xpath=None,
    414                                            abort_check=False):
    415         """Sets the content of a textfield, by passing the xpath.
    416 
    417         @param content: the content to apply to the textfield
    418         @param xpath: the xpath of the textfield
    419         @param wait_for_xpath: an item to wait for before returning, if not
    420                                specified the method does not wait.
    421         @param abort_check: do not get the current value before setting
    422 
    423         """
    424         # When we can get the value we know the text field is ready.
    425         text_field = self.driver.find_element_by_xpath(xpath)
    426         if text_field.get_attribute('type') != 'password' and not abort_check:
    427             try:
    428                 self.wait.until(lambda _: text_field.get_attribute('value'))
    429             except SeleniumTimeoutException, e:
    430                 raise SeleniumTimeoutException('Unable to obtain the value of '
    431                                                'the text field %s.\nWebDriver '
    432                                                'exception:%s' % (xpath, str(e)))
    433         text_field.clear()
    434         text_field.send_keys(content)
    435         if wait_for_xpath: self.wait_for_object_by_xpath(wait_for_xpath)
    436 
    437 
    438     def set_check_box_selected_by_id(self, check_box_id, selected=True,
    439                                      wait_for_xpath=None, alert_handler=None):
    440         """Sets the state of a checkbox, by passing the ID.
    441 
    442         @param check_box_id: the html id of the checkbox
    443         @param selected: True to check the checkbox; False to uncheck it
    444         @param wait_for_xpath: an item to wait for before returning, if not
    445                                specified the method does not wait.
    446         @param alert_handler: method invoked if an alert is detected. The method
    447                               must take one parameter, a webdriver alert object
    448 
    449         """
    450         xpath = 'id("%s")' % check_box_id
    451         self.set_check_box_selected_by_xpath(xpath, selected, wait_for_xpath,
    452                                              alert_handler)
    453 
    454 
    455     def set_check_box_selected_by_xpath(self, xpath, selected=True,
    456                                         wait_for_xpath=None,
    457                                         alert_handler=None):
    458         """Sets the state of a checkbox, by passing the xpath.
    459 
    460         @param xpath: the xpath of the checkbox
    461         @param selected: True to check the checkbox; False to uncheck it
    462         @param wait_for_xpath: an item to wait for before returning, if not
    463                                specified the method does not wait.
    464         @param alert_handler: method invoked if an alert is detected. The method
    465                               must take one parameter, a webdriver alert object
    466         """
    467         check_box = self.wait_for_object_by_xpath(xpath)
    468         value = check_box.get_attribute('value')
    469         if (value == '1' and not selected) or (value == '0' and selected):
    470             check_box.click()
    471         self._handle_alert(xpath, alert_handler)
    472         if wait_for_xpath:
    473             self.wait_for_object_by_xpath(wait_for_xpath)
    474