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