diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..f8d496e --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,26 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python +build: + os: ubuntu-20.04 + tools: + python: "3.10" + +# Optionally set the version of Python and requirements required to build your docs +python: + install: + - requirements: requirements.txt + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: source/conf.py + fail_on_warning: false + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf diff --git a/README.rst b/README.rst index 1ae524c..54d9a43 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,20 @@ Selenium Python Bindings Documentation ====================================== -https://selenium-python.readthedocs.org/en/latest/ +https://selenium-python.readthedocs.io -NOTE: THIS IS NOT AN OFFICIAL DOCUMENTATION +NOTE: THIS IS NOT OFFICIAL DOCUMENTATION -If you would like to contribute to this documentation, you can fork -this project in Github and send pull requests. You can also send your -feedback to my email: baiju.m.mail AT gmail DOT com. +This is not official documentation. If you would like to contribute to this +documentation, you can `fork this project in GitHub and send pull requests +`_. You can also send your feedback +to my email: baiju.m.mail AT gmail DOT com. So far 60+ community members have +contributed to this project (See the 'Closed' pull requests). I encourage +contributors to add more sections to make this documentation even more awesome! +If you know any translations of this document, or would like to create new translations, +please send a PR to update the below list. +**Translations:** + +- `Chinese `_ +- `Japanese `_ diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index d571ab2..0000000 --- a/TODO.txt +++ /dev/null @@ -1,4 +0,0 @@ -- Cover basics of unittest -- XPATH (Appendix) -- Mouse over -- Complete: Test Design Considerations diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..35b84b5 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from setuptools import setup, find_packages + +setup(name="selenium-python-docs", + version="0.0.0", + description="", + packages=find_packages(), + include_package_data=True, + zip_safe=False, + author='Baiju Muthukadan', + install_requires=['setuptools', + 'selenium', + 'Sphinx', + ], + ) diff --git a/source/_static/logo.png b/source/_static/logo.png new file mode 100644 index 0000000..cd765bd Binary files /dev/null and b/source/_static/logo.png differ diff --git a/source/api.rst b/source/api.rst index 8385395..1118aed 100644 --- a/source/api.rst +++ b/source/api.rst @@ -5,17 +5,17 @@ WebDriver API .. note:: - This is not an official documentation. Official API documentation - is available `here - `_. + This is not an official documentation. Official API documentation is + available `here + `_. -This chapter cover all the interfaces of Selenium WebDriver. +This chapter covers all the interfaces of Selenium WebDriver. **Recommended Import Style** -The API definitions in this chapter shows the absolute location of classes. -However the recommended import style is as given below:: +The API definitions in this chapter show the absolute location of classes. +However, the recommended import style is as given below:: from selenium import webdriver @@ -23,42 +23,58 @@ Then, you can access the classes like this:: webdriver.Firefox webdriver.FirefoxProfile + webdriver.FirefoxOptions + webdriver.FirefoxService webdriver.Chrome webdriver.ChromeOptions + webdriver.ChromeService webdriver.Ie - webdriver.Opera - webdriver.PhantomJS + webdriver.IeOptions + webdriver.IeService + webdriver.Edge + webdriver.ChromiumEdge + webdriver.EdgeOptions + webdriver.EdgeService + webdriver.Safari + webdriver.SafariOptions + webdriver.SafariService + webdriver.WebKitGTK + webdriver.WebKitGTKOptions + webdriver.WebKitGTKService + webdriver.WPEWebKit + webdriver.WPEWebKitOptions + webdriver.WPEWebKitService webdriver.Remote webdriver.DesiredCapabilities webdriver.ActionChains - webdriver.TouchActions webdriver.Proxy + webdriver.Keys The special keys class (``Keys``) can be imported like this:: from selenium.webdriver.common.keys import Keys -The exception classes can be imported like this (Replace the ``TheNameOfTheExceptionClass`` with actual class name given below):: +The exception classes can be imported like this (Replace the ``TheNameOfTheExceptionClass`` +with the actual class name given below):: from selenium.common.exceptions import [TheNameOfTheExceptionClass] **Conventions used in the API** Some attributes are callable (or methods) and others are non-callable -(properties). All the callable attributes are ending with round -brackets. +(properties). All the callable attributes are ending with round brackets. Here is an example for property: - current_url - URL of the current loaded page. + URL of the currently loaded page. Usage:: driver.current_url -Here is an example for a method: +Here is an example of a method: - close() @@ -75,6 +91,7 @@ Exceptions .. automodule:: selenium.common.exceptions :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: @@ -85,6 +102,7 @@ Action Chains .. automodule:: selenium.webdriver.common.action_chains :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: @@ -95,6 +113,7 @@ Alerts .. automodule:: selenium.webdriver.common.alert :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: @@ -105,6 +124,75 @@ Special Keys .. automodule:: selenium.webdriver.common.keys :members: :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + + +Locate elements By +~~~~~~~~~~~~~~~~~~ + +These are the attributes which can be used to locate elements. See +the :ref:`locating-elements` chapter for example usages. + +.. automodule:: selenium.webdriver.common.by + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + + +Desired Capabilities +~~~~~~~~~~~~~~~~~~~~ + +See the :ref:`selenium-remote-webdriver` section for example usages of desired capabilities. + +.. automodule:: selenium.webdriver.common.desired_capabilities + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Proxy +~~~~~ + +.. automodule:: selenium.webdriver.common.proxy + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + + +Utilities +~~~~~~~~~ + +.. automodule:: selenium.webdriver.common.utils + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Service +~~~~~~~ + +.. automodule:: selenium.webdriver.common.service + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Application Cache +~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.common.html5.application_cache + :members: + :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: @@ -115,6 +203,47 @@ Firefox WebDriver .. automodule:: selenium.webdriver.firefox.webdriver :members: :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Firefox WebDriver Options +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.firefox.options + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Firefox WebDriver Profile +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.firefox.firefox_profile + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Firefox WebDriver Binary +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.firefox.firefox_binary + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Firefox WebDriver Extension Connection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.firefox.extension_connection + :members: + :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: @@ -124,9 +253,30 @@ Chrome WebDriver .. automodule:: selenium.webdriver.chrome.webdriver :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: +Chrome WebDriver Options +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.chrome.options + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + + +Chrome WebDriver Service +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.chrome.service + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: Remote WebDriver ~~~~~~~~~~~~~~~~ @@ -134,16 +284,157 @@ Remote WebDriver .. automodule:: selenium.webdriver.remote.webdriver :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: - -WebElement -~~~~~~~~~~ +Remote WebDriver WebElement +~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: selenium.webdriver.remote.webelement :members: :undoc-members: + :special-members: __init__ :member-order: groupwise :show-inheritance: +Remote WebDriver Command +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.remote.command + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Remote WebDriver Error Handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.remote.errorhandler + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Remote WebDriver Mobile +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.remote.mobile + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Remote WebDriver Remote Connection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.remote.remote_connection + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Remote WebDriver Utils +~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.remote.utils + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + + +Internet Explorer WebDriver +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.ie.webdriver + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Safari WebDriver +~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.safari.webdriver + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Safari WebDriver Service +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.safari.service + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Select Support +~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.select + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Wait Support +~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.wait + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Color Support +~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.color + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Event Firing WebDriver Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.event_firing_webdriver + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Abstract Event Listener Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.abstract_event_listener + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: + +Expected conditions Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: selenium.webdriver.support.expected_conditions + :members: + :undoc-members: + :special-members: __init__ + :member-order: groupwise + :show-inheritance: diff --git a/source/conf.py b/source/conf.py index dd419ac..3af276f 100644 --- a/source/conf.py +++ b/source/conf.py @@ -41,7 +41,7 @@ # General information about the project. project = u'Selenium Python Bindings' -copyright = u'2011, Baiju Muthukadan' +copyright = u'2011-2024, Baiju Muthukadan' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -91,13 +91,28 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'alabaster' + +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + 'donate.html', + ] +} # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} +html_theme_options = { + 'logo': 'logo.png', + 'show_related': 'true', +} + # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -222,7 +237,7 @@ epub_title = u'Selenium Python Bindings' epub_author = u'Baiju Muthukadan' epub_publisher = u'Baiju Muthukadan' -epub_copyright = u'2010-2011, Baiju Muthukadan' +epub_copyright = u'2011-2018, Baiju Muthukadan' # The language of the text. It defaults to the language option # or en if the language is not set. @@ -242,7 +257,7 @@ # The format is a list of tuples containing the path and title. #epub_pre_files = [] -# HTML files shat should be inserted after the pages created by sphinx. +# HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] diff --git a/source/faq.rst b/source/faq.rst index dcbb2d1..529b1f6 100644 --- a/source/faq.rst +++ b/source/faq.rst @@ -1,36 +1,35 @@ .. _faq: -Appendix: Frequently asked questions +Appendix: Frequently Asked Questions ------------------------------------ -Another FAQ: https://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions +Another FAQ: https://github.com/SeleniumHQ/selenium/wiki/Frequently-Asked-Questions How to use ChromeDriver ? ~~~~~~~~~~~~~~~~~~~~~~~~~ Download the latest `chromedriver from download page -`_. Unzip the +`_. Unzip the file:: - unzip chromedriver_linux32_x.x.x.x.zip + unzip chromedriver_linux64.zip -You should see a ``chromedriver`` executable. Now you can instance of +You should see a ``chromedriver`` executable. Now you can create an instance of Chrome WebDriver like this:: driver = webdriver.Chrome(executable_path="/path/to/chromedriver") -The rest of the example should work as given in other other -documentation. +The rest of the example should work as given in other documentation. -Does Selenium 2 supports XPath 2.0 ? +Does Selenium 2 support XPath 2.0 ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ref: http://seleniumhq.org/docs/03_webdriver.html#how-xpath-works-in-webdriver -Selenium delegate XPath queries down to the browser's own XPath -engine, so Selenium support XPath supports whatever the browser -supports. In browsers which don't have native XPath engines (IE -6,7,8), Selenium support XPath 1.0 only. +Selenium delegates XPath queries down to the browser's own XPath engine, so +Selenium support XPath supports whatever the browser supports. In browsers +which don't have native XPath engines (IE 6,7,8), Selenium supports XPath 1.0 +only. How to scroll down to the bottom of a page ? @@ -38,26 +37,26 @@ How to scroll down to the bottom of a page ? Ref: http://blog.varunin.com/2011/08/scrolling-on-pages-using-selenium.html -You can use the `execute_script` method to execute javascript on the -loaded page. So, you can call the JavaScript API to scroll to the -bottom or any other position of a page. +You can use the `execute_script` method to execute javascript on the loaded +page. So, you can call the JavaScript API to scroll to the bottom or any other +position of a page. Here is an example to scroll to the bottom of a page:: driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") -The `window `_ object -in DOM has a `scrollTo -`_ method to +The `window `_ object in DOM has +a `scrollTo `_ method to scroll to any position of an opened window. The `scrollHeight -`_ is a common -property for all elements. The `document.body.scrollHeight` will give -the height of the entire body of the page. +`_ is a common property for all +elements. The `document.body.scrollHeight` will give the height of the entire +body of the page. How to auto save files using custom Firefox profile ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ref: http://stackoverflow.com/questions/1176348/access-to-file-download-dialog-in-firefox + Ref: http://blog.codecentric.de/en/2010/07/file-downloads-with-selenium-mission-impossible/ The first step is to identify the type of file you want to auto save. @@ -71,10 +70,11 @@ Another way to find content type is using the `requests `_ module, you can use it like this:: import requests - print requests.head('http://www.python.org').headers['content-type'] + content_type = requests.head('http://www.python.org').headers['content-type'] + print(content_type) -Once the content type is identified, you can use it to set the firefox -profile preference: ``browser.helperApps.neverAsk.saveToDisk`` +Once the content type is identified, you can use it to set the firefox profile +preference: ``browser.helperApps.neverAsk.saveToDisk`` Here is an example:: @@ -93,17 +93,24 @@ Here is an example:: browser.get("http://pypi.python.org/pypi/selenium") browser.find_element_by_partial_link_text("selenium-2").click() -In the above example, ``application/octet-stream`` is used as the -content type. +In the above example, ``application/octet-stream`` is used as the content type. + +The ``browser.download.dir`` option specify the directory where you want to +download the files. -The ``browser.download.dir`` option specify the directory where you -want to download the files. +How to upload files into file inputs ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Select the ```` element and call the ``send_keys()`` method +passing the file path, either the path relative to the test script, or an +absolute path. Keep in mind the differences in path names between Windows and +Unix systems. How to use firebug with Firefox ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -First download the Firebug XPI file, later you call the -``add_extension`` method available for the firefox profile:: +First download the Firebug XPI file, later you call the ``add_extension`` method +available for the firefox profile:: from selenium import webdriver @@ -113,3 +120,15 @@ First download the Firebug XPI file, later you call the fp.set_preference("extensions.firebug.currentVersion", "1.8.4") #Avoid startup screen browser = webdriver.Firefox(firefox_profile=fp) +How to take screenshot of the current window ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the `save_screenshot` method provided by the webdriver:: + + from selenium import webdriver + + driver = webdriver.Firefox() + driver.get('http://www.python.org/') + driver.save_screenshot('screenshot.png') + driver.quit() + diff --git a/source/getting-started.rst b/source/getting-started.rst index 92705d9..8c71e49 100644 --- a/source/getting-started.rst +++ b/source/getting-started.rst @@ -6,43 +6,45 @@ Getting Started Simple Usage ~~~~~~~~~~~~ -If you have installed Selenium Python bindings, you can start using it -from Python like this. +If you have installed Selenium Python bindings, you can start using it from +Python like this. :: from selenium import webdriver from selenium.webdriver.common.keys import Keys + from selenium.webdriver.common.by import By driver = webdriver.Firefox() driver.get("http://www.python.org") assert "Python" in driver.title - elem = driver.find_element_by_name("q") - elem.send_keys("selenium") + elem = driver.find_element(By.NAME, "q") + elem.clear() + elem.send_keys("pycon") elem.send_keys(Keys.RETURN) - assert "Google" in driver.title + assert "No results found." not in driver.page_source driver.close() -The above script can be saved into a file (eg:- -`python_org_search.py`), then it can be run like this:: +The above script can be saved into a file (eg:- `python_org_search.py`), then it +can be run like this:: python python_org_search.py -The `python` which you are running should have the `selenium` module -installed. +The `python` which you are running should have the `selenium` module installed. -Walk through of the example -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Example Explained +~~~~~~~~~~~~~~~~~ -The `selenium.webdriver` module provides all the WebDriver -implementations. Currently supported WebDriver implementations are -Firefox, Chrome, Ie and Remote. The `Keys` class provide keys in the -keyboard like RETURN, F1, ALT etc. +The `selenium.webdriver` module provides all the WebDriver implementations. +Currently supported WebDriver implementations are Firefox, Chrome, IE and +Remote. The `Keys` class provide keys in the keyboard like RETURN, F1, ALT etc. +The `By` class is used to locate elements within a document. :: from selenium import webdriver from selenium.webdriver.common.keys import Keys + from selenium.webdriver.common.by import By Next, the instance of Firefox WebDriver is created. @@ -50,43 +52,44 @@ Next, the instance of Firefox WebDriver is created. driver = webdriver.Firefox() -The `driver.get` method will navigate to a page given by the URL. -WebDriver will wait until the page has fully loaded (that is, the -"onload" event has fired) before returning control to your test or -script. It's worth noting that if your page uses a lot of AJAX on -load then WebDriver may not know when it has completely loaded.:: +The `driver.get` method will navigate to a page given by the URL. WebDriver +will wait until the page has fully loaded (that is, the "onload" event has +fired) before returning control to your test or script. *Be aware that if your +page uses a lot of AJAX on load then WebDriver may not know when it has +completely loaded*:: driver.get("http://www.python.org") -The next line is an assertion to confirm that title has "Python" word -in it:: +The next line is an assertion to confirm that title has the word "Python" in it:: assert "Python" in driver.title -WebDriver offers a number of ways to find elements using one of the -`find_element_by_*` methods. For example, the input text element can -be located by its `name` attribute using `find_element_by_name` -method. Detailed explanation of findind elements is available in the -:ref:`locating-elements` chapter:: +WebDriver offers a number of ways to find elements using the +`find_element` method. For example, the input text element can be located +by its `name` attribute using the `find_element` method and using By.NAME as its first parameter. +A detailed explanation of finding elements is available in the :ref:`locating-elements` +chapter:: - elem = driver.find_element_by_name("q") + elem = driver.find_element(By.NAME, "q") -Next we are sending keys, this is similar to entering keys using your -keyboard. Special keys can be send using `Keys` class imported from -`selenium.webdriver.common.keys`:: +Next, we are sending keys, this is similar to entering keys using your keyboard. +Special keys can be sent using the `Keys` class imported from +`selenium.webdriver.common.keys`. To be safe, we'll first clear any +pre-populated text in the input field (e.g. "Search") so it doesn't affect our +search results:: - elem.send_keys("selenium") + elem.clear() + elem.send_keys("pycon") elem.send_keys(Keys.RETURN) -After submission of the page, you should be reached in the Google -site:: +After submission of the page, you should get the result if there is any. To +ensure that some results are found, make an assertion:: - assert "Google" in driver.title + assert "No results found." not in driver.page_source -Finally, the browser window is closed. You can also call `quit` -method instead of `close`. The `quit` will exit entire browser where -as `close` will close one tab, but if it just one tab, by default most -browser will exit entirely.:: +Finally, the browser window is closed. You can also call the `quit` method instead +of `close`. The `quit` method will exit the browser whereas `close` will close one +tab, but if just one tab was open, by default most browsers will exit entirely.:: driver.close() @@ -94,18 +97,19 @@ browser will exit entirely.:: Using Selenium to write tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Selenium is mostly used for writing test cases. The `selenium` -package itself doesn't provide a testing tool/framework. You can -write test cases using Python's unittest module. The other choices as -a tool/framework are py.test and nose. +Selenium is mostly used for writing test cases. The `selenium` package itself +doesn't provide a testing tool/framework. You can write test cases using +Python's unittest module. Alternatively, you may consider `pytest +`_ for writing tests. -In this chapter, we use `unittest` as the framework of choice. Here -is the modified example which uses unittest module. This is a test -for `python.org` search functionality:: +In this chapter, we use `unittest` as the framework of choice. Here is the +modified example which uses the unittest module. This is a test for the `python.org` +search functionality:: import unittest from selenium import webdriver from selenium.webdriver.common.keys import Keys + from selenium.webdriver.common.by import By class PythonOrgSearch(unittest.TestCase): @@ -116,10 +120,11 @@ for `python.org` search functionality:: driver = self.driver driver.get("http://www.python.org") self.assertIn("Python", driver.title) - elem = driver.find_element_by_name("q") - elem.send_keys("selenium") + elem = driver.find_element(By.NAME, "q") + elem.send_keys("pycon") elem.send_keys(Keys.RETURN) - self.assertIn("Google", driver.title) + self.assertNotIn("No results found.", driver.page_source) + def tearDown(self): self.driver.close() @@ -137,95 +142,95 @@ You can run the above test case from a shell like this:: OK -The above results shows that, the test has been successfully -completed. +The above result shows that the test has been successfully completed. + +Note: To run the above test in IPython or Jupyter, you should pass a couple of +arguments to the `main` function as shown below:: + + unittest.main(argv=['first-arg-is-ignored'], exit=False) + -Walk through of the example -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Walkthrough of the example +~~~~~~~~~~~~~~~~~~~~~~~~~~ Initially, all the basic modules required are imported. The `unittest -`_ module is a built-in -Python based on Java's JUnit. This module provides the framework for -organizing the test cases. The `selenium.webdriver` module provides -all the WebDriver implementations. Currently supported WebDriver -implementations are Firefox, Chrome, Ie and Remote. The `Keys` class -provide keys in the keyboard like RETURN, F1, ALT etc. +`_ module is a built-in Python +module based on Java's JUnit. This module provides the framework for organizing the +test cases. The `selenium.webdriver` module provides all the WebDriver +implementations. Currently supported WebDriver implementations are: Firefox, +Chrome, IE and Remote. The `Keys` class provides keys in the keyboard like +RETURN, F1, ALT etc. The `By` class is used to locate elements within a document. :: import unittest from selenium import webdriver from selenium.webdriver.common.keys import Keys + from selenium.webdriver.common.by import By -The test case class is inherited from `unittest.TestCase`. -Inheriting from `TestCase` class is the way to tell `unittest` module -that, this is a test case:: +The test case class is inherited from `unittest.TestCase`. Inheriting from +the `TestCase` class is the way to tell `unittest` module that this is a test case:: class PythonOrgSearch(unittest.TestCase): -The `setUp` is part of initialization, this method will get called -before every test function which you are going to write in this test -case class. Here you are creating the instance of Firefox WebDriver. +The `setUp` method is part of initialization. This method will get called before every +test function which you are going to write in this test case class. Here you +are creating an instance of a Firefox WebDriver. :: def setUp(self): self.driver = webdriver.Firefox() -This is the test case method. The first line inside this method -create a local reference to the driver object created in `setUp` -method. +This is the test case method. The test case method should always start with +characters `test`. The first line inside this method creates a local reference +to the driver object created in `setUp` method. :: def test_search_in_python_org(self): driver = self.driver -The `driver.get` method will navigate to a page given by the URL. -WebDriver will wait until the page has fully loaded (that is, the -"onload" event has fired) before returning control to your test or -script. It's worth noting that if your page uses a lot of AJAX on -load then WebDriver may not know when it has completely loaded.:: +The `driver.get` method will navigate to a page given by the URL. WebDriver +will wait until the page has fully loaded (that is, the "onload" event has +fired) before returning control to your test or script. *Be aware that if your +page uses a lot of AJAX on load then WebDriver may not know when it has +completely loaded*:: driver.get("http://www.python.org") -The next line is an assertion to confirm that title has "Python" word -in it:: +The next line is an assertion to confirm that title has the word "Python" in it:: self.assertIn("Python", driver.title) -.. note:: - The `assertIn` API is only available in Python 2.7 and above. +WebDriver offers a number of ways to find elements using the +`find_element` method. For example, the input text element can be located +by its `name` attribute using the `find_element` method. Detailed +explanation of finding elements is available in the :ref:`locating-elements` +chapter:: -WebDriver offers a number of ways to find elements using one of the -`find_element_by_*` methods. For example, the input text element can -be located by its `name` attribute using `find_element_by_name` -method. Detailed explanation of findind elements is available in the -:ref:`locating-elements` chapter:: + elem = driver.find_element(By.NAME, "q") - elem = driver.find_element_by_name("q") - -Next we are sending keys, this is similar to entering keys using your -keyboard. Special keys can be send using `Keys` class imported from +Next, we are sending keys, this is similar to entering keys using your keyboard. +Special keys can be sent using the `Keys` class imported from `selenium.webdriver.common.keys`:: - elem.send_keys("selenium") + elem.send_keys("pycon") elem.send_keys(Keys.RETURN) -After submission of the page, you should be reached in the Google -site. You can confirm it by asserting "Google" in the title:: +After submission of the page, you should get the result as per search if there +is any. To ensure that some results are found, make an assertion:: - self.assertIn("Google", driver.title) + self.assertNotIn("No results found.", driver.page_source) -The `tearDown` method will get called after every test method. This -is a place to do all cleanup actions. In the current method, the -browser window is closed. You can also call `quit` method instead of -`close`. The `quit` will exit all entire browser where as `close` -will close one tab, but if it just one tab, by default most browser -will exit entirely.:: +The `tearDown` method will get called after every test method. This is a place +to do all cleanup actions. In the current method, the browser window is closed. +You can also call the `quit` method instead of `close`. The `quit` method will exit the +entire browser, whereas `close` will close a tab, but if it is the only tab +opened, by default most browsers will exit entirely.:: def tearDown(self): self.driver.close() @@ -240,39 +245,26 @@ Final lines are some boiler plate code to run the test suite:: Using Selenium with remote WebDriver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To use the remote WebDriver, you should have Selenium server running. -To run the server, use this command:: +To use the remote WebDriver, you should have the Selenium server running. To run +the server, use this command:: java -jar selenium-server-standalone-2.x.x.jar -While running the Selenium server, you could see a message looks like -this:: +While running the Selenium server, you could see a message looking like this:: 15:43:07.541 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub -The above line says that, you can use this URL for connecting to -remote WebDriver. Here are some examples:: - - from selenium.webdriver.common.desired_capabilities import DesiredCapabilities - - driver = webdriver.Remote( - command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities=DesiredCapabilities.CHROME) +The above line says that you can use this URL for connecting to the remote +WebDriver. Here are some examples:: - driver = webdriver.Remote( - command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities=DesiredCapabilities.OPERA) + from selenium import webdriver driver = webdriver.Remote( command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities=DesiredCapabilities.HTMLUNITWITHJS) - -The desired capabilities is a dictionary, so instead of using the -default dictionaries, you can specifies the values explicitly:: + options=webdriver.ChromeOptions() + ) driver = webdriver.Remote( command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities={'browserName': 'htmlunit', - 'version': '2', - 'javascriptEnabled': True}) - + options=webdriver.FirefoxOptions() + ) diff --git a/source/glossary.rst b/source/glossary.rst deleted file mode 100644 index c5d5290..0000000 --- a/source/glossary.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _glossary: - -Glossary --------- - diff --git a/source/index.rst b/source/index.rst index d5f3c9a..8507a6e 100644 --- a/source/index.rst +++ b/source/index.rst @@ -2,13 +2,26 @@ Selenium with Python ******************** -:Author: `Baiju Muthukadan `_ +:Author: `Baiju Muthukadan `_ +:License: This document is licensed under a + `Creative Commons Attribution-ShareAlike 4.0 International License `_. .. note:: - This is not an official documentation. Official API documentation - is available `here - `_. + This is not an official documentation. If you would like to contribute to + this documentation, you can `fork this project in GitHub and send pull + requests `_. You can also send + your feedback to my email: baiju.m.mail AT gmail DOT com. So far 60+ + community members have contributed to this project (See the closed pull + requests). I encourage contributors to add more sections and make it an + awesome documentation! If you know any translation of this document, please + send a PR to update the below list. + + **Translations:** + + - `Chinese `_ + - `Japanese `_ + .. toctree:: :numbered: @@ -17,6 +30,8 @@ Selenium with Python getting-started navigating locating-elements + waits + page-objects api faq diff --git a/source/installation.rst b/source/installation.rst index 6f42689..343bff3 100644 --- a/source/installation.rst +++ b/source/installation.rst @@ -6,124 +6,169 @@ Installation Introduction ~~~~~~~~~~~~ -Selenium Python bindings provides a simple API to write -functional/acceptance tests using Selenium WebDriver. Through -Selenium Python API you can access all functionalities of Selenium -WebDriver in an intuitive way. +Selenium Python bindings provides a simple API to write functional/acceptance +tests using Selenium WebDriver. Through Selenium Python API you can access all +functionalities of Selenium WebDriver in an intuitive way. -Selenium Python bindings provide a convenient API to access Selenium -WebDrivers like Firefox, Ie, Chrome, Remote etc.. The current supported -Python versions are 2.6, 2.7, 3.2 and 3.3. +Selenium Python bindings provide a convenient API to access Selenium WebDrivers +like Firefox, Ie, Chrome, Remote etc. The current supported Python versions are +3.5 and above. -This documentation explains Selenium 2 WebDriver API. Selenium -1 / Selenium RC API is not covered here. +This documentation explains Selenium 2 WebDriver API. Selenium 1 / Selenium RC +API is not covered here. -Downloading Python bindings for Selenium -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Installing Python bindings for Selenium +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can download Python bindings for Selenium from the `PyPI page for -selenium package `_. You can -also use `easy_install -`_ or `pip -`_ to install the bindings:: +Use `pip `_ to install the selenium +package. Python 3 has pip available in the `standard library +`_. Using `pip`, you can +install selenium like this:: - easy_install selenium + pip install selenium -or:: +You may consider using `virtualenv `_ to +create isolated Python environments. Python 3 has `venv +`_ which is almost the same as +virtualenv. - pip install selenium +You can also download Python bindings for Selenium from the `PyPI page for +selenium package `_. and install +manually. -You may consider using `virtualenv `_ -to create isolated Python environments. +Instructions for Windows users +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Detailed instructions for Windows users -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. Install Python 3 using the `MSI available in python.org download page + `_. -.. Note:: +2. Start a command prompt using the ``cmd.exe`` program and run the ``pip`` + command as given below to install `selenium`. - You should have internet connection to perform this installation. + :: -1. Install Python 2.7 using the `MSI available in python.org download - page `_. + C:\Python39\Scripts\pip.exe install selenium -2. Create a folder named ``C:\seltests`` and download `virtualenv.py - `_ - script into that folder. +Now you can run your test scripts using Python. For example, if you have +created a Selenium based script and saved it inside +``C:\my_selenium_script.py``, you can run it like this:: - If you have downloaded and saved the program properly, please make - sure ``virtualenv.py`` file exists at this location in your system: - ``C:\seltests\virtualenv.py`` + C:\Python39\python.exe C:\my_selenium_script.py -3. Start a command prompt (using the ``cmd.exe`` program), then change - to the ``C:\seltests`` folder and run the ``virtualenv.py`` script - as given below. - :: +Installing from Git sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~ - C: - cd C:\seltests - C:\Python27\python.exe virtualenv.py selenv +To build Selenium Python from the source code, clone `the official repository +`_. It contains the source code for +all official Selenium flavors, like Python, Java, Ruby and others. The Python +code resides in the ``/py`` directory. To build, you will also need the `Bazel +`_ build system. +.. note:: - This step will create a folder named ``C:\seltests\selenv`` which - contains a virtual Python. + Currently, as Selenium gets near to the 4.0.0 release, it requires Bazel 3.2.0 + (`Install instructions + `_), even though 3.3.0 + is already available. -4. Use the ``pip`` command as given below to install `selenium` +To build a Wheel from the sources, run the following command from the repository +root:: - :: + bazel //py:selenium-wheel + +This command will prepare the source code with some preprocessed JS files needed +by some webdriver modules and build the ``.whl`` package inside the +``./bazel-bin/py/`` directory. Afterwards, you can use ``pip`` to install it. + +Drivers +~~~~~~~ + +Selenium requires a driver to interface with the chosen browser. Firefox, for +example, requires `geckodriver +`_, which needs to be installed +before the below examples can be run. Make sure it's in your `PATH`, e. g., +place it in `/usr/bin` or `/usr/local/bin`. + +Failure to observe this step will give you an error +`selenium.common.exceptions.WebDriverException: Message: 'geckodriver' +executable needs to be in PATH.` + +Other supported browsers will have their own drivers available. Links to some of +the more popular browser drivers follow. + ++--------------+-----------------------------------------------------------------------+ +| **Chrome**: | https://sites.google.com/chromium.org/driver/ | ++--------------+-----------------------------------------------------------------------+ +| **Edge**: | https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ | ++--------------+-----------------------------------------------------------------------+ +| **Firefox**: | https://github.com/mozilla/geckodriver/releases | ++--------------+-----------------------------------------------------------------------+ +| **Safari**: | https://webkit.org/blog/6900/webdriver-support-in-safari-10/ | ++--------------+-----------------------------------------------------------------------+ - C:\seltests\selenv\Scripts\pip.exe install selenium +For more information about driver installation, please refer the `official +documentation +`_. - Now installation has been completed! You can proceed to test your - Selenium scripts. +Starting from version ``4.6.0`` (November 4, 2022) +selenium comes with **Selenium Manager** packed in distribution. -Now you can run your test scripts using the virtual Python. For -example, if you have created a Selenium based script and saved it inside -``C:\seltests\my_selenium_script.py``, you can run it like this. +**Selenium Manager** is a new tool that helps to get a working environment +to run **Selenium** out of the box: -:: +* automatically discovers, downloads, and caches the ``drivers`` + required by Selenium when these ``drivers`` are unavailable; +* automatically discovers, downloads, and caches the ``browsers`` + driven with Selenium (Chrome, Firefox, and Edge) + when these ``browsers`` are not installed in the local system. - C:\seltests\selenv\Scripts\python.exe C:\seltests\my_selenium_script.py +For example, to see the result of **Selenium Manager** work +just run any selenium script without previous driver setup +and explore `~/.cache/selenium`. +More about **Selenium Manager** you can read in the +`documentation `_ +and +`blog `_. Downloading Selenium server ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: - The Selenium server is only required, if you want to use the remote - WebDriver. See the :ref:`selenium-remote-webdriver` section for - more details. + **The Selenium server is only required if you want to use the remote + WebDriver**. See the :ref:`selenium-remote-webdriver` section for more + details. If you are a beginner learning Selenium, you can skip this section + and proceed with next chapter. -Selenium server is a Java program. Java Runtime Environment (JRE) 1.6 -or newer version is recommended to run Selenium server. +Selenium server is a Java program. Java Runtime Environment (JRE) 1.6 or newer +version is recommended to run Selenium server. -You can download Selenium server 2.x from the `download page of -selenium website `_. The file name -should be something like this: -``selenium-server-standalone-2.x.x.jar``. You can always download the +You can download Selenium server 2.x from the `download page of selenium website +`_. The file name should be something like +this: ``selenium-server-standalone-2.x.x.jar``. You can always download the latest 2.x version of Selenium server. -If Java Runtime Environment (JRE) is not installed in your system, you -can download the `JRE from the Oracle website -`_. -If you are using a GNU/Linux system and have root access in your system, -you can also use your operating system instructions to install JRE. +If Java Runtime Environment (JRE) is not installed in your system, you can +download the `JRE from the Oracle website +`_. If you +are using a GNU/Linux system and have root access in your system, you can also +use your operating system instructions to install JRE. -If `java` command is available in the PATH (environment variable), -you can start the Selenium server using this command:: +If `java` command is available in the PATH (environment variable), you can start +the Selenium server using this command:: java -jar selenium-server-standalone-2.x.x.jar -Replace `2.x.x` with actual version of Selenium server you downloaded -from the site. +Replace `2.x.x` with the actual version of Selenium server you downloaded from +the site. -If JRE is installed as a non-root user and/or if it is -not available in the PATH (environment variable), you can type the -relative or absolute path to the `java` command. Similarly, you can -provide relative or absolute path to Selenium server jar file. -Then, the command will look something like this:: +If JRE is installed as a non-root user and/or if it is not available in the PATH +(environment variable), you can type the relative or absolute path to the `java` +command. Similarly, you can provide a relative or absolute path to Selenium +server jar file. Then, the command will look something like this:: /path/to/java -jar /path/to/selenium-server-standalone-2.x.x.jar diff --git a/source/locating-elements.rst b/source/locating-elements.rst index 4baec26..7bc499e 100644 --- a/source/locating-elements.rst +++ b/source/locating-elements.rst @@ -3,40 +3,60 @@ Locating Elements ----------------- -There are vaious strategies to locate elements in a page. You can use -the most appropriate one for your case. Selenium provides the following -methods to locate elements in a page: - -- `find_element_by_id` -- `find_element_by_name` -- `find_element_by_xpath` -- `find_element_by_link_text` -- `find_element_by_partial_link_text` -- `find_element_by_tag_name` -- `find_element_by_class_name` -- `find_element_by_css_selector` +There are various strategies to locate elements in a page. You can use the most +appropriate one for your case. Selenium provides the following method to +locate elements in a page: +- `find_element` **To find multiple elements (these methods will return a list):** -- `find_elements_by_name` -- `find_elements_by_xpath` -- `find_elements_by_link_text` -- `find_elements_by_partial_link_text` -- `find_elements_by_tag_name` -- `find_elements_by_class_name` -- `find_elements_by_css_selector` +- `find_elements` + + +Example usage:: + + from selenium.webdriver.common.by import By + + driver.find_element(By.XPATH, '//button[text()="Some text"]') + driver.find_elements(By.XPATH, '//button') + +The attributes available for the `By` class are used to locate elements on a page. +These are the attributes available for `By` class:: + + ID = "id" + NAME = "name" + XPATH = "xpath" + LINK_TEXT = "link text" + PARTIAL_LINK_TEXT = "partial link text" + TAG_NAME = "tag name" + CLASS_NAME = "class name" + CSS_SELECTOR = "css selector" + +The 'By' class is used to specify which attribute is used to locate elements on a page. +These are the various ways the attributes are used to locate elements on a page:: + + find_element(By.ID, "id") + find_element(By.NAME, "name") + find_element(By.XPATH, "xpath") + find_element(By.LINK_TEXT, "link text") + find_element(By.PARTIAL_LINK_TEXT, "partial link text") + find_element(By.TAG_NAME, "tag name") + find_element(By.CLASS_NAME, "class name") + find_element(By.CSS_SELECTOR, "css selector") + +If you want to locate several elements with the same attribute replace find_element with find_elements. Locating by Id ~~~~~~~~~~~~~~ -Use this when you know `id` attribute of an element. With this -strategy, the first element with the `id` attribute value matching the -location will be returned. If no element has a matching `id` -attribute, a ``NoSuchElementException`` will be raised. +Use this when you know the `id` attribute of an element. With this strategy, +the first element with a matching `id` attribute will be returned. If no +element has a matching `id` attribute, a ``NoSuchElementException`` will be +raised. -For instance, conside this page source:: +For instance, consider this page source:: @@ -46,22 +66,22 @@ For instance, conside this page source:: - + The form element can be located like this:: - login_form = driver.find_element_by_id('loginForm') + login_form = driver.find_element(By.ID, 'loginForm') Locating by Name ~~~~~~~~~~~~~~~~ -Use this when you know `name` attribute of an element. With this -strategy, the first element with the `name` attribute value matching -the location will be returned. If no element has a matching `name` -attribute, a ``NoSuchElementException`` will be raised. +Use this when you know the `name` attribute of an element. With this strategy, +the first element with a matching `name` attribute will be returned. If no +element has a matching `name` attribute, a ``NoSuchElementException`` will be +raised. -For instance, conside this page source:: +For instance, consider this page source:: @@ -72,44 +92,41 @@ For instance, conside this page source:: - + The username & password elements can be located like this:: - username = driver.find_element_by_name('username') - password = driver.find_element_by_name('password') + username = driver.find_element(By.NAME, 'username') + password = driver.find_element(By.NAME, 'password') -This will give the "Login" button as it occur before the "Clear" -button:: +This will give the "Login" button as it occurs before the "Clear" button:: - continue = driver.find_element_by_name('continue') + continue_button = driver.find_element(By.NAME, 'continue') Locating by XPath ~~~~~~~~~~~~~~~~~ -XPath is the language used for locating nodes in an XML document. As -HTML can be an implementation of XML (XHTML), Selenium users can -leverage this powerful language to target elements in their web -applications. XPath extends beyond (as well as supporting) the simple -methods of locating by id or name attributes, and opens up all sorts -of new possibilities such as locating the third checkbox on the page. - -One of the main reasons for using XPath is when you don't have a -suitable id or name attribute for the element you wish to locate. You -can use XPath to either locate the element in absolute terms (not -advised), or relative to an element that does have an id or name -attribute. XPath locators can also be used to specify elements via -attributes other than id and name. - -Absolute XPaths contain the location of all elements from the root -(html) and as a result are likely to fail with only the slightest -adjustment to the application. By finding a nearby element with an id -or name attribute (ideally a parent element) you can locate your -target element based on the relationship. This is much less likely to -change and can make your tests more robust. - -For instance, conside this page source:: +XPath is the language used for locating nodes in an XML document. As HTML can +be an implementation of XML (XHTML), Selenium users can leverage this powerful +language to target elements in their web applications. XPath supports the +simple methods of locating by id or name attributes and extends them by opening +up all sorts of new possibilities such as locating the third checkbox on the +page. + +One of the main reasons for using XPath is when you don't have a suitable id or +name attribute for the element you wish to locate. You can use XPath to either +locate the element in absolute terms (not advised), or relative to an element +that does have an id or name attribute. XPath locators can also be used to +specify elements via attributes other than id and name. + +Absolute XPaths contain the location of all elements from the root (html) and as +a result are likely to fail with only the slightest adjustment to the +application. By finding a nearby element with an id or name attribute (ideally +a parent element) you can locate your target element based on the relationship. +This is much less likely to change and can make your tests more robust. + +For instance, consider this page source:: @@ -120,66 +137,61 @@ For instance, conside this page source:: - + The form elements can be located like this:: - login_form = driver.find_element_by_xpath("/html/body/form[1]") - login_form = driver.find_element_by_xpath("//form[1]") - login_form = driver.find_element_by_xpath("//form[@id='loginForm']") + login_form = driver.find_element(By.XPATH, "/html/body/form[1]") + login_form = driver.find_element(By.XPATH, "//form[1]") + login_form = driver.find_element(By.XPATH, "//form[@id='loginForm']") 1. Absolute path (would break if the HTML was changed only slightly) 2. First form element in the HTML -3. The form element with attribute named `id` and the value `loginForm` +3. The form element with attribute `id` set to `loginForm` The username element can be located like this:: - username = driver.find_element_by_xpath("//form[input/@name='username']") - username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]") - username = driver.find_element_by_xpath("//input[@name='username']") + username = driver.find_element(By.XPATH, "//form[input/@name='username']") + username = driver.find_element(By.XPATH, "//form[@id='loginForm']/input[1]") + username = driver.find_element(By.XPATH, "//input[@name='username']") -1. First form element with an input child element with attribute named - `name` and the value `username` +1. First form element with an input child element with `name` set to `username` -2. First input child element of the form element with attribute named - `id` and the value `loginForm` +2. First input child element of the form element with attribute `id` set to + `loginForm` -3. First input element with attribute named 'name' and the value - `username` +3. First input element with attribute `name` set to `username` The "Clear" button element can be located like this:: - clear_button = driver.find_element_by_xpath("//input[@name='continue'][@type='button']") - clear_button = driver.find_element_by_xpath("//form[@id='loginForm']/input[4]") + clear_button = driver.find_element(By.XPATH, "//input[@name='continue'][@type='button']") + clear_button = driver.find_element(By.XPATH, "//form[@id='loginForm']/input[4]") -1. Input with attribute named `name` and the value `continue` and - attribute named `type` and the value `button` +1. Input with attribute `name` set to `continue` and attribute `type` set to + `button` -2. Fourth input child element of the form element with attribute named - `id` and value `loginForm` +2. Fourth input child element of the form element with attribute `id` set to + `loginForm` -These examples cover some basics, but in order to learn more, the -following references are recommended: +These examples cover some basics, but in order to learn more, the following +references are recommended: -* `W3Schools XPath Tutorial `_ +* `W3Schools XPath Tutorial `_ * `W3C XPath Recommendation `_ * `XPath Tutorial `_ - with interactive examples. -There are also a couple of very useful Add-ons that can assist in -discovering the XPath of an element: +Here is a couple of very useful Add-ons that can assist in discovering the XPath +of an element: -* `XPath Checker - `_ - - suggests XPath and can be used to test XPath results. -* `Firebug `_ - - XPath suggestions are just one of the many powerful features of this - very useful add-on. +* `xPath Finder + `_ - + Plugin to get the elements xPath. * `XPath Helper `_ - for Google Chrome @@ -188,12 +200,12 @@ discovering the XPath of an element: Locating Hyperlinks by Link Text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use this when you know link text used within an anchor tag. With this -strategy, the first element with the link text value matching the -location will be returned. If no element has a matching link text -attribute, a ``NoSuchElementException`` will be raised. +Use this when you know the link text used within an anchor tag. With this +strategy, the first element with the link text matching the provided value will +be returned. If no element has a matching link text attribute, a +``NoSuchElementException`` will be raised. -For instance, conside this page source:: +For instance, consider this page source:: @@ -201,10 +213,76 @@ For instance, conside this page source:: Continue Cancel - + The continue.html link can be located like this:: - continue_link = driver.find_element_by_link_text('Continue') - continue_link = driver.find_element_by_partial_link_text('Conti') + continue_link = driver.find_element(By.LINK_TEXT, 'Continue') + continue_link = driver.find_element(By.PARTIAL_LINK_TEXT, 'Conti') + + +Locating Elements by Tag Name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use this when you want to locate an element by tag name. With this strategy, +the first element with the given tag name will be returned. If no element has a +matching tag name, a ``NoSuchElementException`` will be raised. + +For instance, consider this page source:: + + + +

Welcome

+

Site content goes here.

+ + + +The heading (h1) element can be located like this:: + + heading1 = driver.find_element(By.TAG_NAME, 'h1') + + +Locating Elements by Class Name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use this when you want to locate an element by class name. With this strategy, +the first element with the matching class name attribute will be returned. If +no element has a matching class name attribute, a ``NoSuchElementException`` +will be raised. + +For instance, consider this page source:: + + + +

Site content goes here.

+ + + +The "p" element can be located like this:: + + content = driver.find_element(By.CLASS_NAME, 'content') + +Locating Elements by CSS Selectors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use this when you want to locate an element using `CSS selector +`_ +syntax. With this strategy, the first element matching the given CSS selector +will be returned. If no element matches the provided CSS selector, a +``NoSuchElementException`` will be raised. + +For instance, consider this page source:: + + + +

Site content goes here.

+ + + +The "p" element can be located like this:: + + content = driver.find_element(By.CSS_SELECTOR, 'p.content') +`Sauce Labs has good documentation +`_ on CSS +selectors. diff --git a/source/navigating.rst b/source/navigating.rst index 3e36949..9da1e0e 100644 --- a/source/navigating.rst +++ b/source/navigating.rst @@ -3,55 +3,53 @@ Navigating ---------- -The first thing you'll want to do with WebDriver is navigate to a -link. The normal way to do this is by calling ``get`` method: +The first thing you'll want to do with WebDriver is navigate to a link. The +normal way to do this is by calling ``get`` method: :: driver.get("http://www.google.com") -WebDriver will wait until the page has fully loaded (that is, the -``onload`` event has fired) before returning control to your test or -script. It's worth noting that if your page uses a lot of AJAX on -load then WebDriver may not know when it has completely loaded. If -you need to ensure such pages are fully loaded then you can use -`waits `_. +WebDriver will wait until the page has fully loaded (that is, the ``onload`` +event has fired) before returning control to your test or script. *Be aware +that if your page uses a lot of AJAX on load then WebDriver may not know when it +has completely loaded*. If you need to ensure such pages are fully loaded then +you can use :ref:`waits `. Interacting with the page ~~~~~~~~~~~~~~~~~~~~~~~~~ -Just being able to go to places isn't terribly useful. What we'd -really like to do is to interact with the pages, or, more -specifically, the HTML elements within a page. First of all, we need -to find one. WebDriver offers a number of ways to find elements. For -example, given an element defined as:: +Just being able to go to places isn't terribly useful. What we'd really like to +do is to interact with the pages, or, more specifically, the HTML elements +within a page. First of all, we need to find one. WebDriver offers a number of +ways to find elements. For example, given an element defined as:: you could find it using any of:: - element = driver.find_element_by_id("passwd-id") - element = driver.find_element_by_name("passwd") - element = driver.find_element_by_xpath("//input[@id='passwd-id']") + element = driver.find_element(By.ID, "passwd-id") + element = driver.find_element(By.NAME, "passwd") + element = driver.find_element(By.XPATH, "//input[@id='passwd-id']") + element = driver.find_element(By.CSS_SELECTOR, "input#passwd-id") -You can also look for a link by its text, but be careful! The text -must be an exact match! You should also be careful when using `XPATH -in WebDriver`. If there's more than one element that matches the -query, then only the first will be returned. If nothing can be found, -a ``NoSuchElementException`` will be raised. +You can also look for a link by its text, but be careful! The text must be an +exact match! You should also be careful when using `XPATH in WebDriver`. If +there's more than one element that matches the query, then only the first will +be returned. If nothing can be found, a ``NoSuchElementException`` will be +raised. .. TODO: Is this following paragraph correct ? -WebDriver has an "Object-based" API; we represent all types of -elements using the same interface. This means that although you may -see a lot of possible methods you could invoke when you hit your IDE's -auto-complete key combination, not all of them will make sense or be -valid. Don't worry! WebDriver will attempt to do the Right Thing, and -if you call a method that makes no sense ("setSelected()" on a "meta" -tag, for example) an exception will be raised. +WebDriver has an "Object-based" API; we represent all types of elements using +the same interface. This means that although you may see a lot of possible +methods you could invoke when you hit your IDE's auto-complete key combination, +not all of them will make sense or be valid. Don't worry! WebDriver will +attempt to do the Right Thing, and if you call a method that makes no sense +("setSelected()" on a "meta" tag, for example) an exception will be raised. -So, you've got an element. What can you do with it? First of all, you -may want to enter some text into a text field:: +So, you've got an element. What can you do with it? First of all, you may want +to enter some text into a text field:: element.send_keys("some text") @@ -59,12 +57,11 @@ You can simulate pressing the arrow keys by using the "Keys" class:: element.send_keys(" and some", Keys.ARROW_DOWN) -It is possible to call `send_keys` on any element, which makes it -possible to test keyboard shortcuts such as those used on GMail. A -side-effect of this is that typing something into a text field won't -automatically clear it. Instead, what you type will be appended to -what's already there. You can easily clear the contents of a text -field or textarea with `clear` method:: +It is possible to call `send_keys` on any element, which makes it possible to +test keyboard shortcuts such as those used on GMail. A side-effect of this is +that typing something into a text field won't automatically clear it. Instead, +what you type will be appended to what's already there. You can easily clear +the contents of a text field or textarea with the `clear` method:: element.clear() @@ -72,47 +69,58 @@ field or textarea with `clear` method:: Filling in forms ~~~~~~~~~~~~~~~~ -We've already seen how to enter text into a textarea or text field, -but what about the other elements? You can "toggle" the state of -checkboxes, and you can use "setSelected" to set something like an -`OPTION` tag selected. Dealing with `SELECT` tags isn't too bad:: +We've already seen how to enter text into a textarea or text field, but what +about the other elements? You can "toggle" the state of the drop down, and you +can use "setSelected" to set something like an `OPTION` tag selected. Dealing +with `SELECT` tags isn't too bad:: - select = driver.find_element_by_xpath("//select") - all_options = select.find_elements_by_tag_name("option") + element = driver.find_element(By.XPATH, "//select[@name='name']") + all_options = element.find_elements(By.TAG_NAME, "option") for option in all_options: - print "Value is: %s" % option.get_attribute("value") + print("Value is: %s" % option.get_attribute("value")) option.click() -This will find the first "SELECT" element on the page, and cycle -through each of it's OPTIONs in turn, printing out their values, and -selecting each in turn. +This will find the first "SELECT" element on the page, and cycle through each of +its OPTIONs in turn, printing out their values, and selecting each in turn. -.. As you can see, this isn't the most efficient -.. way of dealing with SELECT elements . WebDriver's support classes -.. include one called "Select", which provides useful methods for -.. interacting with these. +As you can see, this isn't the most efficient way of dealing with SELECT +elements. WebDriver's support classes include one called a "Select", which +provides useful methods for interacting with these:: -.. :: + from selenium.webdriver.support.ui import Select + select = Select(driver.find_element(By.NAME, 'name')) + select.select_by_index(index) + select.select_by_visible_text("text") + select.select_by_value(value) -.. select = driver.find_element_by_xpath("//select").select() #<- FIXME: API -.. select.deselectAll() #<- FIXME: API -.. select.selectByVisibleText("Edam") #<- FIXME: API -.. This will deselect all OPTIONs from the first SELECT on the page, and -.. then select the OPTION with the displayed text of "Edam". +WebDriver also provides features for deselecting all the selected options:: -Once you've finished filling out the form, you probably want to submit -it. One way to do this would be to find the "submit" button and click -it:: + select = Select(driver.find_element(By.ID, 'id')) + select.deselect_all() + +This will deselect all OPTIONs from that particular SELECT on the page. + +Suppose in a test, we need the list of all default selected options, Select +class provides a property method that returns a list:: + + select = Select(driver.find_element(By.XPATH, "//select[@name='name']")) + all_selected_options = select.all_selected_options + +To get all available options:: + + options = select.options + +Once you've finished filling out the form, you probably want to submit it. One +way to do this would be to find the "submit" button and click it:: # Assume the button has the ID "submit" :) - driver.find_element_by_id("submit").click() + driver.find_element(By.ID, "submit").click() -Alternatively, WebDriver has the convenience method "submit" on every -element. If you call this on an element within a form, WebDriver will -walk up the DOM until it finds the enclosing form and then calls -submit on that. If the element isn't in a form, then the -``NoSuchElementException`` will be raised:: +Alternatively, WebDriver has the convenience method "submit" on every element. +If you call this on an element within a form, WebDriver will walk up the DOM +until it finds the enclosing form and then calls submit on that. If the element +isn't in a form, then the ``NoSuchElementException`` will be raised:: element.submit() @@ -120,94 +128,97 @@ submit on that. If the element isn't in a form, then the Drag and drop ~~~~~~~~~~~~~ -You can use drag and drop, either moving an element by a certain -amount, or on to another element:: +You can use drag and drop, either moving an element by a certain amount, or on +to another element:: - element = driver.find_element_by_name("source") - target = driver.find_element_by_name("target") + element = driver.find_element(By.NAME, "source") + target = driver.find_element(By.NAME, "target") from selenium.webdriver import ActionChains action_chains = ActionChains(driver) - action_chains.drag_and_drop(element, target) + action_chains.drag_and_drop(element, target).perform() Moving between windows and frames ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It's rare for a modern web application not to have any frames or to be -constrained to a single window. WebDriver supports moving between -named windows using the "switch_to_window" method:: +constrained to a single window. WebDriver supports moving between named windows +using the "switch_to.window" method:: - driver.switch_to_window("windowName") + driver.switch_to.window("windowName") -All calls to ``driver`` will now be interpreted as being directed to -the particular window. But how do you know the window's name? Take a -look at the javascript or link that opened it:: +All calls to ``driver`` will now be interpreted as being directed to the +particular window. But how do you know the window's name? Take a look at the +javascript or link that opened it:: Click here to open a new window -Alternatively, you can pass a "window handle" to the -"switch_to_window()" method. Knowing this, it's possible to iterate -over every open window like so:: +Alternatively, you can pass a "window handle" to the "switch_to.window()" +method. Knowing this, it's possible to iterate over every open window like so:: for handle in driver.window_handles: - driver.switch_to_window(handle) + driver.switch_to.window(handle) You can also swing from frame to frame (or into iframes):: - driver.switch_to_frame("frameName") + driver.switch_to.frame("frameName") -It's possible to access subframes by separating the path with a dot, -and you can specify the frame by its index too. That is:: +It's possible to access subframes by separating the path with a dot, and you can +specify the frame by its index too. That is:: - driver.switch_to_frame("frameName.0.child") + driver.switch_to.frame("frameName.0.child") -would go to the frame named "child" of the first subframe of the frame -called "frameName". **All frames are evaluated as if from *top*.** +would go to the frame named "child" of the first subframe of the frame called +"frameName". **All frames are evaluated as if from *top*.** + +Once we are done with working on frames, we will have to come back to the parent +frame which can be done using:: + + driver.switch_to.default_content() Popup dialogs ~~~~~~~~~~~~~ -Selenium WebDriver has built-in support for handling popup dialog -boxes. After you've triggerd and action that would open a popup, you -can access the alert with the following:: +Selenium WebDriver has built-in support for handling popup dialog boxes. After +you've triggered action that would open a popup, you can access the alert with +the following:: - alert = driver.switch_to_alert() + alert = driver.switch_to.alert -This will return the currently open alert object. With this object -you can now accept, dismiss, read its contents or even type into a -prompt. This interface works equally well on alerts, confirms, -prompts. Refer to the API documentation for more information. +This will return the currently open alert object. With this object, you can now +accept, dismiss, read its contents or even type into a prompt. This interface +works equally well on alerts, confirms, prompts. Refer to the API documentation +for more information. Navigation: history and location ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Earlier, we covered navigating to a page using the "get" command ( -``driver.get("http://www.example.com")``) As you've seen, WebDriver -has a number of smaller, task-focused interfaces, and navigation is a -useful task. To navigate to a page, you can use `get` method:: +``driver.get("http://www.example.com")``). As you've seen, WebDriver has a +number of smaller, task-focused interfaces, and navigation is a useful task. To +navigate to a page, you can use `get` method:: driver.get("http://www.example.com") -To move backwards and forwards in your browser's history:: +To move backward and forward in your browser's history:: driver.forward() driver.back() -Please be aware that this functionality depends entirely on the -underlying driver. It's just possible that something unexpected may -happen when you call these methods if you're used to the behaviour of -one browser over another. +Please be aware that this functionality depends entirely on the underlying +driver. It's just possible that something unexpected may happen when you call +these methods if you're used to the behavior of one browser over another. Cookies ~~~~~~~ -Before we leave these next steps, you may be interested in -understanding how to use cookies. First of all, you need to be on the -domain that the cookie will be valid for: +Before moving to the next section of the tutorial, you may be interested in +understanding how to use cookies. First of all, you need to be on the domain +that the cookie will be valid for: :: @@ -215,19 +226,8 @@ domain that the cookie will be valid for: driver.get("http://www.example.com") # Now set the cookie. This one's valid for the entire domain - cookie = {"key": "value"}) + cookie = {'name' : 'foo', 'value' : 'bar'} driver.add_cookie(cookie) # And now output all the available cookies for the current URL - all_cookies = driver.get_cookies() - for cookie_name, cookie_value in all_cookies.items(): - print "%s -> %s", cookie_name, cookie_value - - -Next, next steps! -~~~~~~~~~~~~~~~~~ - -This has been a high level walkthrough of WebDriver and some of its -key capabilities. You may want to look at the `Test Design -Considerations` chapter to get some ideas about how you can reduce the -pain of maintaining your tests and how to make your code more modular. + driver.get_cookies() diff --git a/source/page-objects.rst b/source/page-objects.rst new file mode 100644 index 0000000..e628ad7 --- /dev/null +++ b/source/page-objects.rst @@ -0,0 +1,167 @@ +.. _page-objects: + +Page Objects +------------ + +This chapter is a tutorial introduction to the Page Objects design pattern. A +page object represents an area where the test interacts within the web +application user interface. + +Benefits of using page object pattern: + +* Easy to read test cases +* Creating reusable code that can share across multiple test cases +* Reducing the amount of duplicated code +* If the user interface changes, the fix needs changes in only one place + + +Test case +~~~~~~~~~ + +Here is a test case that searches for a word on the `python.org` website and +ensures some results. The following section will introduce the `page` module +where the page objects will be defined. + +:: + + import unittest + from selenium import webdriver + import page + + class PythonOrgSearch(unittest.TestCase): + """A sample test class to show how page object works""" + + def setUp(self): + self.driver = webdriver.Firefox() + self.driver.get("http://www.python.org") + + def test_search_in_python_org(self): + """Tests python.org search feature. Searches for the word "pycon" then + verified that some results show up. Note that it does not look for + any particular text in search results page. This test verifies that + the results were not empty.""" + + #Load the main page. In this case the home page of Python.org. + main_page = page.MainPage(self.driver) + #Checks if the word "Python" is in title + self.assertTrue(main_page.is_title_matches(), "python.org title doesn't match.") + #Sets the text of search textbox to "pycon" + main_page.search_text_element = "pycon" + main_page.click_go_button() + search_results_page = page.SearchResultsPage(self.driver) + #Verifies that the results page is not empty + self.assertTrue(search_results_page.is_results_found(), "No results found.") + + def tearDown(self): + self.driver.close() + + if __name__ == "__main__": + unittest.main() + + +Page object classes +~~~~~~~~~~~~~~~~~~~ + +The page object pattern intends to create an object for each part of a web page. +This technique helps build a separation between the test code and the actual +code that interacts with the web page. + +The ``page.py`` will look like this:: + + from element import BasePageElement + from locators import MainPageLocators + + class SearchTextElement(BasePageElement): + """This class gets the search text from the specified locator""" + + #The locator for search box where search string is entered + locator = 'q' + + + class BasePage(object): + """Base class to initialize the base page that will be called from all + pages""" + + def __init__(self, driver): + self.driver = driver + + + class MainPage(BasePage): + """Home page action methods come here. I.e. Python.org""" + + #Declares a variable that will contain the retrieved text + search_text_element = SearchTextElement() + + def is_title_matches(self): + """Verifies that the hardcoded text "Python" appears in page title""" + + return "Python" in self.driver.title + + def click_go_button(self): + """Triggers the search""" + + element = self.driver.find_element(*MainPageLocators.GO_BUTTON) + element.click() + + + class SearchResultsPage(BasePage): + """Search results page action methods come here""" + + def is_results_found(self): + # Probably should search for this text in the specific page + # element, but as for now it works fine + return "No results found." not in self.driver.page_source + + +Page elements +~~~~~~~~~~~~~ + +The ``element.py`` will look like this:: + + from selenium.webdriver.common.by import By + from selenium.webdriver.support.ui import WebDriverWait + + + class BasePageElement(object): + """Base page class that is initialized on every page object class.""" + + def __set__(self, obj, value): + """Sets the text to the value supplied""" + + driver = obj.driver + WebDriverWait(driver, 100).until( + lambda driver: driver.find_element(By.NAME, self.locator)) + driver.find_element(By.NAME, self.locator).clear() + driver.find_element(By.NAME, self.locator).send_keys(value) + + def __get__(self, obj, owner): + """Gets the text of the specified object""" + + driver = obj.driver + WebDriverWait(driver, 100).until( + lambda driver: driver.find_element(By.NAME, self.locator)) + element = driver.find_element(By.NAME, self.locator) + return element.get_attribute("value") + + +Locators +~~~~~~~~ + +One of the practices is to separate the locator strings from the place where +they are getting used. In this example, locators of the same page belong to the +same class. + +The ``locators.py`` will look like this:: + + from selenium.webdriver.common.by import By + + class MainPageLocators(object): + """A class for main page locators. All main page locators should come here""" + + GO_BUTTON = (By.ID, 'submit') + + class SearchResultsPageLocators(object): + """A class for search results locators. All search results locators should + come here""" + + pass diff --git a/source/references.rst b/source/references.rst deleted file mode 100644 index 069ce8e..0000000 --- a/source/references.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _references: - -References ----------- - -- Official API: http://selenium.googlecode.com/svn/trunk/docs/api/py/index.html - -- Blog post explaining how to use headless X for running Selenium - tests: - http://coreygoldberg.blogspot.com/2011/06/python-headless-selenium-webdriver.html - -- Jenkins plugin for headless Selenium tests: - https://wiki.jenkins-ci.org/display/JENKINS/Xvnc+Plugin - diff --git a/source/test-design.rst b/source/test-design.rst deleted file mode 100644 index f9083cb..0000000 --- a/source/test-design.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _test-design: - -Test Design Considerations --------------------------- - -Page Objects -~~~~~~~~~~~~ - -Page objects is a design pattern used for web automated testing. - -.. - Typical structure recommended. - - tests/pageobject/page.py - tests/pageobject/element.py - tests/package1/locators.py - tests/pakage1/page/somepage.py - tests/pakage1/test_something.py - tests/package2/locators.py - tests/pakage2/page/somepage.py - tests/pakage2/test_something.py - -Few links: - -1. http://code.google.com/p/selenium/wiki/PageObjects -2. http://www.theautomatedtester.co.uk/tutorials/selenium/page-object-pattern.htm -3. http://pragprog.com/magazines/2010-08/page-objects-in-python -4. http://docs.seleniumhq.org/docs/06_test_design_considerations.jsp diff --git a/source/waits.rst b/source/waits.rst new file mode 100644 index 0000000..1e0c50f --- /dev/null +++ b/source/waits.rst @@ -0,0 +1,144 @@ +.. _waits: + +Waits +----- + +These days, most of the web apps are using AJAX techniques. When a page is +loaded by the browser, the elements within that page may load at different time +intervals. This makes locating elements difficult: if an element is not yet +present in the DOM, a locate function will raise an `ElementNotVisibleException` +exception. Using waits, we can solve this issue. Waiting provides some slack +between actions performed - mostly locating an element or any other operation +with the element. + +Selenium Webdriver provides two types of waits - implicit & explicit. An +explicit wait makes WebDriver wait for a certain condition to occur before +proceeding further with execution. An implicit wait makes WebDriver poll the +DOM for a certain amount of time when trying to locate an element. + + +Explicit Waits +~~~~~~~~~~~~~~ + +An explicit wait is a code you define to wait for a certain condition to occur +before proceeding further in the code. The extreme case of this is +time.sleep(), which sets the condition to an exact time period to wait. There +are some convenience methods provided that help you write code that will wait +only as long as required. WebDriverWait in combination with ExpectedCondition +is one way this can be accomplished. + +:: + + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.support.wait import WebDriverWait + from selenium.webdriver.support import expected_conditions as EC + + driver = webdriver.Firefox() + driver.get("http://somedomain/url_that_delays_loading") + try: + element = WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.ID, "myDynamicElement")) + ) + finally: + driver.quit() + + +In the code above, Selenium will wait for a maximum of 10 seconds for an element +matching the given criteria to be found. If no element is found in that time, a +TimeoutException is thrown. By default, WebDriverWait calls the +ExpectedCondition every 500 milliseconds until it returns success. +ExpectedCondition will return `true` (Boolean) in case of success or `not null` +if it fails to locate an element. + +**Expected Conditions** + +There are some common conditions that are frequently of use when automating web +browsers. Listed below are the names of each. Selenium Python binding provides +some `convenience methods +`_ +so you don't have to code an expected_condition class yourself or create your +own utility package for them. + +- title_is +- title_contains +- presence_of_element_located +- visibility_of_element_located +- visibility_of +- presence_of_all_elements_located +- text_to_be_present_in_element +- text_to_be_present_in_element_value +- frame_to_be_available_and_switch_to_it +- invisibility_of_element_located +- element_to_be_clickable +- staleness_of +- element_to_be_selected +- element_located_to_be_selected +- element_selection_state_to_be +- element_located_selection_state_to_be +- alert_is_present + +:: + + from selenium.webdriver.support import expected_conditions as EC + + wait = WebDriverWait(driver, 10) + element = wait.until(EC.element_to_be_clickable((By.ID, 'someid'))) + +The expected_conditions module contains a set of predefined conditions to use +with WebDriverWait. + +**Custom Wait Conditions** + +You can also create custom wait conditions when none of the previous convenience +methods fit your requirements. A custom wait condition can be created using a +class with `__call__` method which returns `False` when the condition doesn't +match. + + +:: + + class element_has_css_class(object): + """An expectation for checking that an element has a particular css class. + + locator - used to find the element + returns the WebElement once it has the particular css class + """ + def __init__(self, locator, css_class): + self.locator = locator + self.css_class = css_class + + def __call__(self, driver): + element = driver.find_element(*self.locator) # Finding the referenced element + if self.css_class in element.get_attribute("class"): + return element + else: + return False + + # Wait until an element with id='myNewInput' has class 'myCSSClass' + wait = WebDriverWait(driver, 10) + element = wait.until(element_has_css_class((By.ID, 'myNewInput'), "myCSSClass")) + + +.. note:: **polling2 Library** + + You may also consider using `polling2 + `_ + library which you need to install separately. + +Implicit Waits +~~~~~~~~~~~~~~ + +An implicit wait tells WebDriver to poll the DOM for a certain amount of time +when trying to find any element (or elements) not immediately available. The +default setting is 0 (zero). Once set, the implicit wait is set for the life of +the WebDriver object. + +:: + + from selenium import webdriver + + driver = webdriver.Firefox() + driver.implicitly_wait(10) # seconds + driver.get("http://somedomain/url_that_delays_loading") + myDynamicElement = driver.find_element_by_id("myDynamicElement")