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 be39960..54d9a43 100644 --- a/README.rst +++ b/README.rst @@ -3,11 +3,18 @@ Selenium Python Bindings Documentation https://selenium-python.readthedocs.io -NOTE: 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 40+ community members have contributed to this -project (See the closed pull requests). I encourage contributors -to add more sections and make it a good documentation! +NOTE: THIS IS NOT OFFICIAL DOCUMENTATION + +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/source/api.rst b/source/api.rst index decd474..1118aed 100644 --- a/source/api.rst +++ b/source/api.rst @@ -5,8 +5,8 @@ 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 covers all the interfaces of Selenium WebDriver. @@ -23,16 +23,32 @@ 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:: @@ -46,8 +62,7 @@ with the actual class name given below):: **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: @@ -140,16 +155,6 @@ See the :ref:`selenium-remote-webdriver` section for example usages of desired c :member-order: groupwise :show-inheritance: -Touch Actions -~~~~~~~~~~~~~ - -.. automodule:: selenium.webdriver.common.touch_actions - :members: - :undoc-members: - :special-members: __init__ - :member-order: groupwise - :show-inheritance: - Proxy ~~~~~ @@ -182,7 +187,7 @@ Service :show-inheritance: Application Cache -~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~ .. automodule:: selenium.webdriver.common.html5.application_cache :members: @@ -354,46 +359,6 @@ Internet Explorer WebDriver :member-order: groupwise :show-inheritance: -Android WebDriver -~~~~~~~~~~~~~~~~~ - -.. automodule:: selenium.webdriver.android.webdriver - :members: - :undoc-members: - :special-members: __init__ - :member-order: groupwise - :show-inheritance: - -Opera WebDriver -~~~~~~~~~~~~~~~ - -.. automodule:: selenium.webdriver.opera.webdriver - :members: - :undoc-members: - :special-members: __init__ - :member-order: groupwise - :show-inheritance: - -PhantomJS WebDriver -~~~~~~~~~~~~~~~~~~~ - -.. automodule:: selenium.webdriver.phantomjs.webdriver - :members: - :undoc-members: - :special-members: __init__ - :member-order: groupwise - :show-inheritance: - -PhantomJS WebDriver Service -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: selenium.webdriver.phantomjs.service - :members: - :undoc-members: - :special-members: __init__ - :member-order: groupwise - :show-inheritance: - Safari WebDriver ~~~~~~~~~~~~~~~~ diff --git a/source/conf.py b/source/conf.py index 316e761..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-2018, 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 @@ -257,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 b7f865a..529b1f6 100644 --- a/source/faq.rst +++ b/source/faq.rst @@ -9,10 +9,10 @@ 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 create an instance of Chrome WebDriver like this:: @@ -26,10 +26,10 @@ Does Selenium 2 support XPath 2.0 ? Ref: http://seleniumhq.org/docs/03_webdriver.html#how-xpath-works-in-webdriver -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. +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 ? @@ -37,21 +37,20 @@ 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 ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -74,8 +73,8 @@ Another way to find content type is using the `requests 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:: @@ -94,24 +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. +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 diff --git a/source/getting-started.rst b/source/getting-started.rst index 0599af4..8c71e49 100644 --- a/source/getting-started.rst +++ b/source/getting-started.rst @@ -6,44 +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 = driver.find_element(By.NAME, "q") elem.clear() elem.send_keys("pycon") elem.send_keys(Keys.RETURN) 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. 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. @@ -51,46 +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. A detailed explanation of finding 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 sent 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`. 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:: +pre-populated text in the input field (e.g. "Search") so it doesn't affect our +search results:: elem.clear() elem.send_keys("pycon") elem.send_keys(Keys.RETURN) -After submission of the page, you should get the result if there is any. -To ensure that some results are found, make an assertion:: +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 "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 whereas -close` will close one tab, but if just one tab was open, 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() @@ -98,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 options for -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): @@ -120,10 +120,10 @@ 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 = driver.find_element(By.NAME, "q") elem.send_keys("pycon") elem.send_keys(Keys.RETURN) - assert "No results found." not in driver.page_source + self.assertNotIn("No results found.", driver.page_source) def tearDown(self): @@ -142,93 +142,95 @@ You can run the above test case from a shell like this:: OK -The above result 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:: -Walk through of the example -~~~~~~~~~~~~~~~~~~~~~~~~~~~ + unittest.main(argv=['first-arg-is-ignored'], exit=False) + + + +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 test case method should always start -with characters `test`. 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) -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 finding 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. 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 +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("pycon") elem.send_keys(Keys.RETURN) -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:: +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:: - assert "No results found." not in driver.page_source + 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 the entire browser, whereas `close` -will close a tab, but if it is the only tab opened, 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() @@ -243,40 +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 looking 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:: +The above line says that you can use this URL for connecting to the remote +WebDriver. Here are some examples:: from selenium import webdriver - 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) - - driver = webdriver.Remote( - command_executor='http://127.0.0.1:4444/wd/hub', - desired_capabilities=DesiredCapabilities.OPERA) 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 specify 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/index.rst b/source/index.rst index 1fa627f..8507a6e 100644 --- a/source/index.rst +++ b/source/index.rst @@ -8,14 +8,19 @@ Selenium with Python .. note:: - 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 - 40+ community members have contributed to this project - (See the closed pull requests). I encourage contributors to add - more sections and make it a good documentation! + 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:: diff --git a/source/installation.rst b/source/installation.rst index 2f2da2b..343bff3 100644 --- a/source/installation.rst +++ b/source/installation.rst @@ -6,49 +6,101 @@ 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.7, 3.5 and above. +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 `_. However, -a better approach would be to use -`pip `_ to -install the selenium package. Python 3.6 has pip available in the -`standard library `_. -Using `pip`, you can install selenium like this:: +Use `pip `_ to install the selenium +package. Python 3 has pip available in the `standard library +`_. Using `pip`, you can +install selenium like this:: pip install selenium -You may consider using `virtualenv `_ -to create isolated Python environments. Python 3.6 has `pyvenv -`_ -which is almost same as virtualenv. +You may consider using `virtualenv `_ to +create isolated Python environments. Python 3 has `venv +`_ which is almost the same as +virtualenv. + +You can also download Python bindings for Selenium from the `PyPI page for +selenium package `_. and install +manually. + + +Instructions for Windows users +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install Python 3 using the `MSI available in python.org download page + `_. + +2. Start a command prompt using the ``cmd.exe`` program and run the ``pip`` + command as given below to install `selenium`. + + :: + + C:\Python39\Scripts\pip.exe install selenium + +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:: + + C:\Python39\python.exe C:\my_selenium_script.py + + +Installing from Git sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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:: + + 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. + +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`. +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.` +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. +Other supported browsers will have their own drivers available. Links to some of +the more popular browser drivers follow. +--------------+-----------------------------------------------------------------------+ -| **Chrome**: | https://sites.google.com/a/chromium.org/chromedriver/downloads | +| **Chrome**: | https://sites.google.com/chromium.org/driver/ | +--------------+-----------------------------------------------------------------------+ | **Edge**: | https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ | +--------------+-----------------------------------------------------------------------+ @@ -57,30 +109,30 @@ Other supported browsers will have their own drivers available. Links to some of | **Safari**: | https://webkit.org/blog/6900/webdriver-support-in-safari-10/ | +--------------+-----------------------------------------------------------------------+ +For more information about driver installation, please refer the `official +documentation +`_. -Detailed instructions for Windows users -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. Note:: - - You should have an internet connection to perform this installation. - -1. Install Python 3.6 using the `MSI available in python.org download - page `_. +Starting from version ``4.6.0`` (November 4, 2022) +selenium comes with **Selenium Manager** packed in distribution. -2. Start a command prompt using the ``cmd.exe`` program and run the - ``pip`` command as given below to install `selenium`. +**Selenium Manager** is a new tool that helps to get a working environment +to run **Selenium** out of the box: - :: - - C:\Python35\Scripts\pip.exe install selenium - -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:: +* 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:\Python35\python.exe C:\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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -88,37 +140,35 @@ 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. If you are a beginner learning Selenium, you can - skip this section and proceed with next chapter. + 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 the 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 a 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 a218b78..7bc499e 100644 --- a/source/locating-elements.rst +++ b/source/locating-elements.rst @@ -3,34 +3,16 @@ Locating Elements ----------------- -There are various 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` -Apart from the public methods given above, there are two private -methods which might be useful with locators in page objects. These -are the two private methods: `find_element` and `find_elements`. Example usage:: @@ -39,26 +21,40 @@ Example usage:: 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" - NAME = "name" 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, consider this page source:: @@ -70,20 +66,20 @@ For instance, consider 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, consider this page source:: @@ -96,42 +92,39 @@ For instance, consider 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 occurs 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. +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:: @@ -144,50 +137,48 @@ For instance, consider 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 `_ * `W3C XPath Recommendation `_ @@ -195,15 +186,12 @@ following references are recommended: `_ - 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 @@ -212,10 +200,10 @@ 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, consider this page source:: @@ -225,21 +213,20 @@ For instance, consider 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. +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:: @@ -248,20 +235,20 @@ 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') + heading1 = driver.find_element(By.TAG_NAME, 'h1') Locating Elements by Class Name ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use this when you want to locate an element by class attribute name. -With this strategy, the first element with the matching class attribute -name will be returned. If no element has a matching class attribute name, -a ``NoSuchElementException`` will be raised. +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:: @@ -269,19 +256,20 @@ 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') + content = driver.find_element(By.CLASS_NAME, 'content') Locating Elements by CSS Selectors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use this when you want to locate an element by CSS selector syntax. -With this strategy, the first element with the matching CSS selector -will be returned. If no element has a matching CSS selector, -a ``NoSuchElementException`` will be raised. +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:: @@ -289,11 +277,12 @@ 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') + content = driver.find_element(By.CSS_SELECTOR, 'p.content') -`Sauce Labs has good documentation `_ -on CSS selectors. +`Sauce Labs has good documentation +`_ on CSS +selectors. diff --git a/source/navigating.rst b/source/navigating.rst index b00dbd0..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 -:ref:`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 the `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,28 +69,26 @@ field or textarea with the `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 the -drop down, 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:: - element = driver.find_element_by_xpath("//select[@name='name']") - all_options = element.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")) option.click() -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. +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 a "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(driver.find_element(By.NAME, 'name')) select.select_by_index(index) select.select_by_visible_text("text") select.select_by_value(value) @@ -101,7 +96,7 @@ interacting with these:: WebDriver also provides features for deselecting all the selected options:: - select = Select(driver.find_element_by_id('id')) + select = Select(driver.find_element(By.ID, 'id')) select.deselect_all() This will deselect all OPTIONs from that particular SELECT on the page. @@ -109,25 +104,23 @@ 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']")) + 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:: +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() @@ -135,77 +128,78 @@ 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).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:: -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() - driver.switch_to_default_content() Popup dialogs ~~~~~~~~~~~~~ -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:: +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") @@ -214,18 +208,17 @@ 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 behavior 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: :: @@ -233,9 +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 = {‘name’ : ‘foo’, ‘value’ : ‘bar’} + cookie = {'name' : 'foo', 'value' : 'bar'} driver.add_cookie(cookie) # And now output all the available cookies for the current URL driver.get_cookies() - diff --git a/source/page-objects.rst b/source/page-objects.rst index 822b2ec..e628ad7 100644 --- a/source/page-objects.rst +++ b/source/page-objects.rst @@ -3,13 +3,14 @@ Page Objects ------------ -This chapter is a tutorial introduction to page objects design -pattern. A page object represents an area in the web application user -interface that your test is interacting. +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: -* Creating reusable code that can be shared across multiple test cases +* 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 @@ -17,8 +18,9 @@ Benefits of using page object pattern: Test case ~~~~~~~~~ -Here is a test case which searches for a word in python.org website -and ensure some results are found. +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. :: @@ -34,22 +36,21 @@ and ensure some results are found. 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. - """ + """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 - assert main_page.is_title_matches(), "python.org title doesn't match." + 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 - assert search_results_page.is_results_found(), "No results found." + self.assertTrue(search_results_page.is_results_found(), "No results found.") def tearDown(self): self.driver.close() @@ -57,12 +58,13 @@ and ensure some results are found. if __name__ == "__main__": unittest.main() + Page object classes ~~~~~~~~~~~~~~~~~~~ -The page object pattern intends creating an object for each web page. -By following this technique a layer of separation between the test -code and technical implementation is created. +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:: @@ -77,7 +79,8 @@ The ``page.py`` will look like this:: class BasePage(object): - """Base class to initialize the base page that will be called from all pages""" + """Base class to initialize the base page that will be called from all + pages""" def __init__(self, driver): self.driver = driver @@ -91,10 +94,12 @@ The ``page.py`` will look like this:: 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() @@ -107,11 +112,13 @@ The ``page.py`` will look like this:: # 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 @@ -120,26 +127,29 @@ The ``element.py`` will look like this:: 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) + 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) + 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 being used. In this example, locators of the same page -belong to same class. +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:: @@ -147,8 +157,11 @@ The ``locators.py`` will look like this:: 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""" + """A class for search results locators. All search results locators should + come here""" + pass diff --git a/source/waits.rst b/source/waits.rst index 821ee58..1e0c50f 100644 --- a/source/waits.rst +++ b/source/waits.rst @@ -3,38 +3,35 @@ 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. +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. +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.ui import WebDriverWait + from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Firefox() @@ -47,19 +44,20 @@ accomplished. driver.quit() -This waits up to 10 seconds before throwing a TimeoutException unless -it finds the element to return within 10 seconds. WebDriverWait -by default calls the ExpectedCondition every 500 milliseconds until it -returns successfully. A successful return is for ExpectedCondition -type is Boolean return true or not null return value for all other -ExpectedCondition types. +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 +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 @@ -87,14 +85,15 @@ own utility package for them. 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. +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. +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. :: @@ -121,14 +120,19 @@ with `__call__` method which returns `False` when the condition doesn't match. 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. Once set, the -implicit wait is set for the life of the WebDriver object. +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. ::