From 309bf2e8c1993c2f0b34ac9dde5a44c0aaa79db5 Mon Sep 17 00:00:00 2001 From: David Lai Date: Fri, 25 Oct 2013 14:18:01 -0700 Subject: [PATCH] Add some usage examples to doc strings Signed-off-by: Andreas Tolfsen --- py/selenium/common/exceptions.py | 39 +++- py/selenium/webdriver/common/action_chains.py | 60 +++++- py/selenium/webdriver/common/alert.py | 28 ++- .../webdriver/common/desired_capabilities.py | 21 +++ py/selenium/webdriver/common/touch_actions.py | 7 + py/selenium/webdriver/remote/webelement.py | 172 ++++++++++++++++-- 6 files changed, 299 insertions(+), 28 deletions(-) diff --git a/py/selenium/common/exceptions.py b/py/selenium/common/exceptions.py index a580673e85db8..656c02d03e44e 100644 --- a/py/selenium/common/exceptions.py +++ b/py/selenium/common/exceptions.py @@ -63,18 +63,34 @@ class NoSuchFrameException(InvalidSwitchToTargetException): class NoSuchWindowException(InvalidSwitchToTargetException): """ Thrown when window target to be switched doesn't exist. + + To find the current set of active window handles, you can get a list + of the active window handles in the following way:: + + print driver.window_handles + """ pass class NoSuchElementException(WebDriverException): """ Thrown when element could not be found. + + If you encounter this exception, you may want to check the following: + * Check your selector used in your find_by... + * Element may not yet be on the screen at the time of the find operation, + (webpage is still loading) see selenium.webdriver.support.wait.WebDriverWait() + for how to write a wait wrapper to wait for an element to appear. """ pass class NoSuchAttributeException(WebDriverException): """ Thrown when the attribute of element could not be found. + + You may want to check if the attribute exists in the particular browser you are + testing against. Some browsers may have different property names for the same + property. (IE8's .innerText vs. Firefox .textContent) """ pass @@ -83,6 +99,16 @@ class StaleElementReferenceException(WebDriverException): Thrown when a reference to an element is now "stale". Stale means the element no longer appears on the DOM of the page. + + + Possible causes of StaleElementReferenceException include, but not limited to: + * You are no longer on the same page, or the page may have refreshed since the element + was located. + * The element may have been removed and re-added to the screen, since it was located. + Such as an element being relocated. + This can happen typically with a javascript framework when values are updated and the + node is rebuilt. + * Element may have been inside an iframe or another context which was refreshed. """ pass @@ -94,25 +120,36 @@ class InvalidElementStateException(WebDriverException): class UnexpectedAlertPresentException(WebDriverException): """ Thrown when an unexpected alert is appeared. + + Usually raised when when an expected modal is blocking webdriver form executing any + more commands. """ pass class NoAlertPresentException(WebDriverException): """ Thrown when switching to no presented alert. + + This can be caused by calling an operation on the Alert() class when an alert is + not yet on the screen. """ pass class ElementNotVisibleException(InvalidElementStateException): """ - Thrown when although an element is present on the DOM, + Thrown when an element is present on the DOM, but it is not visible, and so is not able to be interacted with. + + Most commonly encountered when trying to click or read text + of an element that is hidden from view. """ pass class ElementNotSelectableException(InvalidElementStateException): """ Thrown when trying to select an unselectable element. + + For example, selecting a 'script' element. """ pass diff --git a/py/selenium/webdriver/common/action_chains.py b/py/selenium/webdriver/common/action_chains.py index 89511f0ee6c3c..46af732692ddb 100755 --- a/py/selenium/webdriver/common/action_chains.py +++ b/py/selenium/webdriver/common/action_chains.py @@ -21,9 +21,35 @@ class ActionChains(object): """ + ActionChains are a way to automate low level interactions such as + mouse movements, mouse button actions, key press, and context menu interactions. + This is useful for doing more complex actions like hover over and drag and drop. + Generate user actions. - All actions are stored in the ActionChains object. - Call perform() to fire stored actions. + When you call methods for actions on the ActionChains object, + the actions are stored in a queue in the ActionChains object. + When you call perform(), the events are fired in the order they + are queued up. + + ActionChains can be used in a chain pattern:: + + menu = driver.find_element_by_css_selector(".nav") + hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1") + + ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform() + + Or actions can be queued up one by one, then performed.:: + + menu = driver.find_element_by_css_selector(".nav") + hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1") + + actions = ActionChains(driver) + actions.move_to_element(menu) + actions.click(hidden_submenu) + actions.perform() + + Either way, the actions are performed in the order they are called, one after + another. """ def __init__(self, driver): @@ -132,6 +158,11 @@ def key_down(self, value, element=None): - value: The modifier key to send. Values are defined in `Keys` class. - element: The element to send keys. If None, sends a key to current focused element. + + Example, pressing ctrl+c:: + + ActionsChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() + """ if element: self.click(element) self._actions.append(lambda: @@ -147,6 +178,11 @@ def key_up(self, value, element=None): - value: The modifier key to send. Values are defined in Keys class. - element: The element to send keys. If None, sends a key to current focused element. + + Example, pressing ctrl+c:: + + ActionsChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform() + """ if element: self.click(element) self._actions.append(lambda: @@ -159,8 +195,8 @@ def move_by_offset(self, xoffset, yoffset): Moving the mouse to an offset from current mouse position. :Args: - - xoffset: X offset to move to. - - yoffset: Y offset to move to. + - xoffset: X offset to move to, as a positive or negative integer. + - yoffset: Y offset to move to, as a positive or negative integer. """ self._actions.append(lambda: self._driver.execute(Command.MOVE_TO, { @@ -173,7 +209,7 @@ def move_to_element(self, to_element): Moving the mouse to the middle of an element. :Args: - - to_element: The element to move to. + - to_element: The WebElement to move to. """ self._actions.append(lambda: self._driver.execute(Command.MOVE_TO, {'element': to_element.id})) @@ -185,7 +221,7 @@ def move_to_element_with_offset(self, to_element, xoffset, yoffset): Offsets are relative to the top-left corner of the element. :Args: - - to_element: The element to move to. + - to_element: The WebElement to move to. - xoffset: X offset to move to. - yoffset: Y offset to move to. """ @@ -214,7 +250,8 @@ def send_keys(self, *keys_to_send): Sends keys to current focused element. :Args: - - keys_to_send: The keys to send. + - keys_to_send: The keys to send. Modifier keys constants can be found in the + 'Keys' class. """ self._actions.append(lambda: self._driver.execute(Command.SEND_KEYS_TO_ACTIVE_ELEMENT, @@ -227,7 +264,8 @@ def send_keys_to_element(self, element, *keys_to_send): :Args: - element: The element to send keys. - - keys_to_send: The keys to send. + - keys_to_send: The keys to send. Modifier keys constants can be found in the + 'Keys' class. """ self._actions.append(lambda: element.send_keys(*keys_to_send)) @@ -247,3 +285,9 @@ def _keys_to_typing(self, value): typing.append(val[i]) return typing + # Context manager so ActionChains can be used in a 'with .. as' statements. + def __enter__(self): + return self # Return created instance of self. + + def __exit__(self, _type, _value, _traceback): + pass # Do nothing, does not require additional cleanup. diff --git a/py/selenium/webdriver/common/alert.py b/py/selenium/webdriver/common/alert.py index d176ec89272ff..7bbec8e222881 100644 --- a/py/selenium/webdriver/common/alert.py +++ b/py/selenium/webdriver/common/alert.py @@ -22,7 +22,28 @@ class Alert(object): """ - Allows to work wit alerts. + Allows to work with alerts. + + Use this class to interact with alert prompts. It contains methods for dismissing, + accepting, inputting, and getting text from alert prompts. + + Accepting / Dismissing alert prompts:: + + Alert(driver).accept() + Alert(driver).dismiss() + + Inputting a value into an alert prompt: + + name_prompt = Alert(driver) + name_prompt.send_keys("Willian Shakesphere") + name_prompt.accept() + + + Reading a the text of a prompt for verification: + + alert_text = Alert(driver).text + self.assertEqual("Do you wish to quit?", alert_text) + """ def __init__(self, driver): @@ -50,6 +71,9 @@ def dismiss(self): def accept(self): """ Accepts the alert available. + + Usage:: + Alert(driver).accept() # Confirm a alert dialog. """ self.driver.execute(Command.ACCEPT_ALERT) @@ -59,5 +83,7 @@ def send_keys(self, keysToSend): :Args: - keysToSend: The text to be sent to Alert. + + """ self.driver.execute(Command.SET_ALERT_VALUE, {'text': keysToSend}) diff --git a/py/selenium/webdriver/common/desired_capabilities.py b/py/selenium/webdriver/common/desired_capabilities.py index 914346150719c..acf4ef8d49e06 100755 --- a/py/selenium/webdriver/common/desired_capabilities.py +++ b/py/selenium/webdriver/common/desired_capabilities.py @@ -20,6 +20,27 @@ class DesiredCapabilities(object): """ Set of supported desired capabilities. + + Use this as a starting point for creating a desired capabilities object for + requesting remote webdrivers from selenium server, or selenium grid. + + + Usage Example: + + from selenium import webdriver + + selenim_grid_url = "http://198.0.0.1:4444/wd/hub" + + # Create a desired capabilities object as a starting point. + capabilities = DesiredCapabilities.FIREFOX + capabilities['platform'] = "WINDOWS" + capabilities['version'] = "10" + + # Request a remote webdriver the the desired capabilities. + driver = webdriver.Remote(desired_capabilities=capabilities, + command_executor=selenium_grid_url) + + """ FIREFOX = { diff --git a/py/selenium/webdriver/common/touch_actions.py b/py/selenium/webdriver/common/touch_actions.py index d40c16b37a7d7..6f5837abf8b90 100755 --- a/py/selenium/webdriver/common/touch_actions.py +++ b/py/selenium/webdriver/common/touch_actions.py @@ -165,3 +165,10 @@ def flick_element(self, on_element, xoffset, yoffset, speed): 'yoffset': int(yoffset), 'speed': int(speed)})) return self + + # Context manager so TouchActions can be used in a 'with .. as' statements. + def __enter__(self): + return self # Return created instance of self. + + def __exit__(self, _type, _value, _traceback): + pass # Do nothing, does not require additional cleanup. \ No newline at end of file diff --git a/py/selenium/webdriver/remote/webelement.py b/py/selenium/webdriver/remote/webelement.py index 95eb21084afb5..d3e8384243fcd 100755 --- a/py/selenium/webdriver/remote/webelement.py +++ b/py/selenium/webdriver/remote/webelement.py @@ -67,7 +67,17 @@ def clear(self): self._execute(Command.CLEAR_ELEMENT) def get_attribute(self, name): - """Gets the attribute value.""" + """Gets the attribute value. + + :Args: + - name - name of the attribute property to retieve. + + Example:: + + # Check if the 'active' css class is applied to an element. + is_active = "active" in target_element.get_attribute("class") + + """ resp = self._execute(Command.GET_ELEMENT_ATTRIBUTE, {'name': name}) attributeValue = '' if resp['value'] is None: @@ -80,7 +90,10 @@ def get_attribute(self, name): return attributeValue def is_selected(self): - """Whether the element is selected.""" + """Whether the element is selected. + + Can be used to check if a checkbox or radio button is selected. + """ return self._execute(Command.IS_ELEMENT_SELECTED)['value'] def is_enabled(self): @@ -88,64 +101,176 @@ def is_enabled(self): return self._execute(Command.IS_ELEMENT_ENABLED)['value'] def find_element_by_id(self, id_): - """Finds element by id.""" + """Finds element within the child elements of this element. + + :Args: + - id_ - ID of child element to locate. + """ return self.find_element(by=By.ID, value=id_) def find_elements_by_id(self, id_): + """Finds a list of elements within the children of this element + with the matching ID. + + :Args: + - id_ - Id of child element to find. + """ return self.find_elements(by=By.ID, value=id_) def find_element_by_name(self, name): - """Find element by name.""" + """Find element with in this element's children by name. + :Args: + - name - name property of the element to find. + """ return self.find_element(by=By.NAME, value=name) def find_elements_by_name(self, name): + """Finds a list of elements with in this element's children by name. + + :Args: + - name - name property to search for. + """ return self.find_elements(by=By.NAME, value=name) def find_element_by_link_text(self, link_text): - """Finds element by link text.""" + """Finds element with in this element's children by visible link text. + + :Args: + - link_text - Link text string to search for. + """ return self.find_element(by=By.LINK_TEXT, value=link_text) def find_elements_by_link_text(self, link_text): + """Finds a list of elements with in this element's children by visible link text. + + :Args: + - link_text - Link text string to search for. + """ return self.find_elements(by=By.LINK_TEXT, value=link_text) def find_element_by_partial_link_text(self, link_text): + """Finds element with in this element's children by parial visible link text. + + :Args: + - link_text - Link text string to search for. + """ return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text) def find_elements_by_partial_link_text(self, link_text): + """Finds a list of elements with in this element's children by link text. + + :Args: + - link_text - Link text string to search for. + """ return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text) def find_element_by_tag_name(self, name): + """Finds element with in this element's children by tag name. + + :Args: + - name - name of html tag (eg: h1, a, span) + """ return self.find_element(by=By.TAG_NAME, value=name) def find_elements_by_tag_name(self, name): + """Finds a list of elements with in this element's children by tag name. + + :Args: + - name - name of html tag (eg: h1, a, span) + """ return self.find_elements(by=By.TAG_NAME, value=name) def find_element_by_xpath(self, xpath): - """Finds element by xpath.""" + """Finds element by xpath. + + :Args: + xpath - xpath of element to locate. "//input[@class='myelement']" + + Note: The base path will be relative to this element's location. + + This will select the first link under this element.:: + + myelement.find_elements_by_xpath(".//a") + + However, this will select the first link on the page. + + myelement.find_elements_by_xpath("//a") + + """ return self.find_element(by=By.XPATH, value=xpath) def find_elements_by_xpath(self, xpath): - """Finds elements within the elements by xpath.""" + """Finds elements within the elements by xpath. + + :Args: + - xpath - xpath locator string. + + Note: The base path will be relative to this element's location. + + This will select all links under this element.:: + + myelement.find_elements_by_xpath(".//a") + + However, this will select all links in the page itself. + + myelement.find_elements_by_xpath("//a") + """ return self.find_elements(by=By.XPATH, value=xpath) def find_element_by_class_name(self, name): - """Finds an element by their class name.""" + """Finds an element within this element's children by their class name. + + :Args: + - name - class name to search on. + """ return self.find_element(by=By.CLASS_NAME, value=name) def find_elements_by_class_name(self, name): - """Finds elements by their class name.""" + """Finds a list of elements within children of this element by their class name. + + :Args: + - name - class name to search on. + """ return self.find_elements(by=By.CLASS_NAME, value=name) def find_element_by_css_selector(self, css_selector): - """Find and return an element by CSS selector.""" + """Find and return an element that's a child of this element by CSS selector. + + :Args: + - css_selector - CSS selctor string, ex: 'a.nav#home' + """ return self.find_element(by=By.CSS_SELECTOR, value=css_selector) def find_elements_by_css_selector(self, css_selector): - """Find and return list of multiple elements by CSS selector.""" + """Find and return list of multiple elements within the children of this + element by CSS selector. + + :Args: + - css_selector - CSS selctor string, ex: 'a.nav#home' + """ return self.find_elements(by=By.CSS_SELECTOR, value=css_selector) def send_keys(self, *value): - """Simulates typing into the element.""" + """Simulates typing into the element. + + :Args: + - value - A string for typing, or setting form fields. For setting + file inputs, this could be a local file path. + + Use this to send simple key events or to fill out form fields:: + + form_textfield = driver.find_element_by_name('username') + form_textfield.send_keys("admin") + + This can also be used to set file inputs.:: + + file_input = driver.find_element_by_name('profilePic') + file_input.send_keys("path/to/profilepic.gif") + # Generally it's better to wrap the file path in one of the methods + # in os.path to return the actual path to support cross OS testing. + # file_input.send_keys(os.path.abspath("path/to/profilepic.gif")) + + """ # transfer file to another machine only if remote driver is used # the same behaviour as for java binding if self.parent._is_remote: @@ -168,7 +293,8 @@ def send_keys(self, *value): # RenderedWebElement Items def is_displayed(self): - """Whether the element would be visible to a user""" + """Whether the element would be visible to a user + """ return self._execute(Command.IS_ELEMENT_DISPLAYED)['value'] @property @@ -204,10 +330,20 @@ def location(self): @property def parent(self): + """ Returns parent element is available. """ return self._parent @property def id(self): + """ Returns internal id used by selenium. + + This is mainly for internal use. Simple use cases such as checking if 2 webelements + refer to the same element, can be done using '==':: + + if element1 == element2: + print("These 2 are equal") + + """ return self._id def __eq__(self, element): @@ -235,14 +371,14 @@ def _execute(self, command, params=None): def find_element(self, by=By.ID, value=None): if not By.is_valid(by) or not isinstance(value, str): raise InvalidSelectorException("Invalid locator values passed in") - + return self._execute(Command.FIND_CHILD_ELEMENT, {"using": by, "value": value})['value'] def find_elements(self, by=By.ID, value=None): if not By.is_valid(by) or not isinstance(value, str): raise InvalidSelectorException("Invalid locator values passed in") - + return self._execute(Command.FIND_CHILD_ELEMENTS, {"using": by, "value": value})['value'] @@ -286,9 +422,9 @@ def is_local_file(cls, *keys): return None try: - if os.path.isfile(file_path): - return file_path + if os.path.isfile(file_path): + return file_path except: - pass + pass return None