|
| 1 | +Extending SeleniumLibrary |
| 2 | +========================= |
| 3 | + |
| 4 | +.. contents:: |
| 5 | + |
| 6 | +Introduction |
| 7 | +------------ |
| 8 | +Starting from SeleniumLibrary 3.0, the library has moved to use Robot Framework |
| 9 | +`dynamic library API`_. To ease the usage of the dynamic library API, the SeleniumLibrary uses |
| 10 | +a `PythonLibCore`_ project to handle the keyword keyword discovery, running the keywords and |
| 11 | +other things to related to the dynamic library API. For more details please read the Robot |
| 12 | +Framework `dynamic library API`_ documentation. |
| 13 | + |
| 14 | +Public API methods |
| 15 | +------------------ |
| 16 | +All the methods, which are exposed as keywords, are available in the library public API. Generally |
| 17 | +keywords are converted to lover case and spaces are converted to underscores. Example `Open Browser`_ |
| 18 | +keyword is available as ``open_browser`` method. Please note that method name can be overwritten |
| 19 | +with the ``@keyword`` decorator, but in the SeleniumLibrary 3.0.0 release does not contain keywords |
| 20 | +where the keyword name would differ from the method name (other than the keywords name case). |
| 21 | + |
| 22 | +Methods or attributes which are not keywords but are available in the public API |
| 23 | +-------------------------------------------------------------------------------- |
| 24 | +The SeleniumLibrary instance also contains methods and attributes which are not keywords but are |
| 25 | +useful when extending the SeleniumLibrary. The available methods are: |
| 26 | + |
| 27 | +================ ============================================================= |
| 28 | + Method Description |
| 29 | +================ ============================================================= |
| 30 | +find_element Finds first element matching ``locator``. |
| 31 | +find_elements Find all elements matching ``locator``. |
| 32 | +register_driver Add's a ``driver`` to the library WebDriverCache. |
| 33 | +run_keyword Responsible for executing keywords. |
| 34 | +failure_occurred Method that is executed when a SeleniumLibrary keyword fails. |
| 35 | +================ ============================================================= |
| 36 | + |
| 37 | +Also there are the following public attributes available: |
| 38 | + |
| 39 | +========================= ================================================================ |
| 40 | + Attribute Description |
| 41 | +========================= ================================================================ |
| 42 | +driver Current active driver. |
| 43 | +timeout Default value for ``timeouts`` used with ``Wait ...`` keywords. |
| 44 | +implicit_wait Default value for ``implicit wait`` used when locating elements. |
| 45 | +run_on_failure_keyword Default action for the `run-on-failure functionality`. |
| 46 | +screenshot_root_directory Location where possible screenshots are created |
| 47 | +========================= ================================================================ |
| 48 | + |
| 49 | +For more details about the methods, please read the individual method documentation and many |
| 50 | +of the attributes are explained in the library `keyword documentation`_. |
| 51 | + |
| 52 | +Extending SeleniumLibrary by pull requests |
| 53 | +------------------------------------------ |
| 54 | +Before making your own extension, private or public, please consider would the extension be |
| 55 | +generally useful in the SeleniumLibrary. If the extension would be useful for others, then please |
| 56 | +create an issue and a pull request. More details how SeleniumLibrary project handles the |
| 57 | +enhancement requests can be read from the CONTRIBUTING.rst `Enhancement requests`_ chapter. |
| 58 | + |
| 59 | +General prinsibles for extending SeleniumLibrary |
| 60 | +------------------------------------------------ |
| 61 | +The prinsibles described in the Robot Framework User Guide, `Extending existing test libraries`_ |
| 62 | +chapter also apply when extending the SeleniumLibrary. There are two different ways to |
| 63 | +extend the SeleniumLibrary. |
| 64 | + |
| 65 | +1) Create a library which also the contains existing SeleniumLibrary keywords, example by using `inheritance`_. |
| 66 | +2) Create library which contains only new keywords. |
| 67 | + |
| 68 | +When creating a library, which also includes the existing SeleniumLibrary keywords, there are |
| 69 | +extra steps which needs to be taken account, because SeleniunLibrary uses `PythonLibCore`_ |
| 70 | +and the `dynamic library API`_. All methods which should be published as keywords must be |
| 71 | +decorated with ``@keyword`` decorator. The ``@keyword`` decorator can be imported in following way:: |
| 72 | + |
| 73 | + from SeleniumLibrary.base import keyword |
| 74 | + |
| 75 | +Keywords should be inside of a ``class`` and the ``add_library_components`` method |
| 76 | +must be called to add keywords. The ``add_library_components`` method is inherited from the |
| 77 | +`PythonLibCore`_ project and the method must contains list of classes which contains the |
| 78 | +new keywords. |
| 79 | + |
| 80 | +Creating a new library by using inheritance |
| 81 | +------------------------------------------- |
| 82 | +Perhaps the easiest way to extend the SeleniumLibrary is to inherit the SeleniumLibrary and add |
| 83 | +new keywords methods to a new library. The `inheritance example`_ shows how to declare new |
| 84 | +keyword ``Get Browser Desired Capabilities`` and how to overwrite existing ``Open Browser`` keyword. |
| 85 | + |
| 86 | +Because the ``InheritSeleniumLibrary`` class foes not overwrite the SeleniumLibrary ``init`` method, |
| 87 | +the ``add_library_components`` is called automatically. Then the ``InheritSeleniumLibrary`` class methods |
| 88 | +which are decorated with ``@keyword`` decorator are added to the ``InheritSeleniumLibrary`` |
| 89 | +library keywords. Also existing keywords from SeleniumLibrary are added as library keywords. |
| 90 | + |
| 91 | +Because the methods are not anymore directly available in the SeleniumLibrary class, it not |
| 92 | +anymore possible to call the original method example like this:: |
| 93 | + |
| 94 | + super(ClassName, self).open_browser(url, browser, alias, remote_url, |
| 95 | + desired_capabilities, ff_profile_dir) |
| 96 | + |
| 97 | +Instead user must call the method from the class instance which implements the keyword, example:: |
| 98 | + |
| 99 | + browser_management = BrowserManagementKeywords(self) |
| 100 | + browser_management.open_browser(url, 'chrome') |
| 101 | + |
| 102 | +Creating a new library from multiple classes |
| 103 | +-------------------------------------------- |
| 104 | +Decomposition is a good way to split library to smaller name spaces and it usually eases the |
| 105 | +library testing. The `decomposition example`_ shows how the ``Get Browser Desired Capabilities`` |
| 106 | +and ``Open Browser`` keywords can divided to own classes. |
| 107 | + |
| 108 | +The example also shows the usage of the ``context`` object and the `LibraryComponent`_ class. |
| 109 | +The ``context`` object is a instance of the SeleniunLibrary which provides access to the |
| 110 | +SeleniumLibrary methods for the ``BrowserKeywords`` and ``DesiredCapabilitiesKeywords`` classes. |
| 111 | +Example ``context`` object provides access to the Selenium WebDriver instance. |
| 112 | + |
| 113 | +The ``LibraryComponent`` is a wrapper class, which provides easier shortcuts the ``context`` object |
| 114 | +methods and example provides general logging methods. Example the Selenium WebDriver instance in |
| 115 | +the context: ``self.ctx.driver``, but the ``LibraryComponent`` provides a shortcut and it can be |
| 116 | +accessed with: ``self.driver`` |
| 117 | + |
| 118 | + |
| 119 | +Creating a new library by getting active library instance |
| 120 | +--------------------------------------------------------- |
| 121 | +Getting the active library instance provides way to create a new library that it does not |
| 122 | +automatically contain keywords from the SeleniumLibrary. This eases the name space |
| 123 | +handling and if only new keywords are created, user does not have to prefix the keywords with the |
| 124 | +library name. This way also allows user to freely choose the Robot Framework `library API`_. |
| 125 | +The `instance example`_ shows a way how the active SeleniumLibrary is get from the Robot Framework. |
| 126 | +The example shows how to declare ``Get Browser Desired Capabilities`` and ``Open Browser`` keywords |
| 127 | +to the new library and the `instance example`_ uses the `static keyword API`_ to declare new |
| 128 | +keywords. |
| 129 | + |
| 130 | +Extending the SeleniumLibrary dynamically |
| 131 | +----------------------------------------- |
| 132 | +It is possible to modify keywords directly in the SeleniumLibrary. This can be done calling |
| 133 | +``add_library_components`` method from the SeleniumLibrary instance and then using the |
| 134 | +``BuiltIn().reload_library()`` method to reload the SeleniumLibrary. |
| 135 | + |
| 136 | +The `modify SeleniumLibrary`_ example uses the Robot Framework `Listener API`_ to extend the |
| 137 | +SeleniumLibrary with a keywords in the ``KeywordClass``. The ``NewKeywords``class is library |
| 138 | +which is also a listener and it uses the ``start_suite`` method to trigger the modifications |
| 139 | +to the SeleniumLibrary. The example gets the active library instance from the Robot Framework |
| 140 | +and uses the ``add_library_components`` method to add the ``KeywordClass`` keywords in to |
| 141 | +the SeleniumLibrary directly. |
| 142 | + |
| 143 | +.. _dynamic library API: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#dynamic-library-api |
| 144 | +.. _PythonLibCore: https://github.com/robotframework/PythonLibCore |
| 145 | +.. _Open Browser: http://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Open%20Browser |
| 146 | +.. _keyword documentation: http://robotframework.org/SeleniumLibrary/SeleniumLibrary.html |
| 147 | +.. _Enhancement requests: https://github.com/robotframework/SeleniumLibrary/blob/master/CONTRIBUTING.rst#enhancement-requests |
| 148 | +.. _Extending existing test libraries: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#extending-existing-test-libraries |
| 149 | +.. _inheritance: https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/examples/inheritance/InheritSeleniumLibrary.py |
| 150 | +.. _inheritance example: https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/examples/inheritance/InheritSeleniumLibrary.py |
| 151 | +.. _decomposition example: https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/examples/decomposition/Decomposition.py |
| 152 | +.. _instance example: https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/examples/get_instance/GetSeleniumLibraryInstance.py |
| 153 | +.. _LibraryComponent: https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/base/librarycomponent.py |
| 154 | +.. _library API: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#different-test-library-apis |
| 155 | +.. _static keyword API: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#creating-static-keywords |
| 156 | +.. _modify SeleniumLibrary: https://github.com/robotframework/SeleniumLibrary/blob/master/docs/extending/examples/modify_seleniumlibrary/NewKeywords.py |
| 157 | +.. _Listener API: http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#listener-interface |
0 commit comments