basics.rst (6927B)
1 .. py:currentmodule:: marionette_driver.marionette 2 3 Marionette Python Client 4 ======================== 5 6 The Marionette Python client library allows you to remotely control a 7 Gecko-based browser or device which is running a Marionette_ 8 server. This includes Firefox Desktop and Firefox for Android. 9 10 The Marionette server is built directly into Gecko and can be started by 11 passing in a command line option to Gecko, or by using a Marionette-enabled 12 build. The server listens for connections from various clients. Clients can 13 then control Gecko by sending commands to the server. 14 15 This is the official Python client for Marionette. There also exists a 16 `NodeJS client`_ maintained by the Firefox OS automation team. 17 18 .. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette 19 .. _NodeJS client: https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette 20 21 Getting the Client 22 ------------------ 23 24 The Python client is officially supported. To install it, first make sure you 25 have `pip installed`_ then run: 26 27 .. code-block:: bash 28 29 $ pip install marionette_driver 30 31 It's highly recommended to use virtualenv_ when installing Marionette to avoid 32 package conflicts and other general nastiness. 33 34 You should now be ready to start using Marionette. The best way to learn is to 35 play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up 36 a python shell and follow along with the 37 :doc:`interactive tutorial <interactive>`! 38 39 .. _pip installed: https://pip.pypa.io/en/latest/installing.html 40 .. _virtualenv: http://virtualenv.readthedocs.org/en/latest/ 41 .. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds 42 43 Using the Client for Testing 44 ---------------------------- 45 46 Please visit the `Marionette Tests`_ section on MDN for information regarding 47 testing with Marionette. 48 49 .. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests 50 51 Session Management 52 ------------------ 53 A session is a single instance of a Marionette client connected to a Marionette 54 server. Before you can start executing commands, you need to start a session 55 with :func:`start_session() <Marionette.start_session>`: 56 57 .. code-block:: python 58 59 from marionette_driver.marionette import Marionette 60 61 client = Marionette('127.0.0.1', port=2828) 62 client.start_session() 63 64 This returns a session id and an object listing the capabilities of the 65 Marionette server. For example, a server running on Firefox Desktop will 66 have some features which a server running from Firefox Android won't. 67 It's also possible to access the capabilities using the 68 :attr:`~Marionette.session_capabilities` attribute. After finishing with a 69 session, you can delete it with :func:`~Marionette.delete_session()`. Note that 70 this will also happen automatically when the Marionette object is garbage 71 collected. 72 73 Context Management 74 ------------------ 75 Commands can only be executed in a single window, frame and scope at a time. In 76 order to run commands elsewhere, it's necessary to explicitly switch to the 77 appropriate context. 78 79 Use :func:`~Marionette.switch_to_window` to execute commands in the context of a 80 new window: 81 82 .. code-block:: python 83 84 original_window = client.current_window_handle 85 for handle in client.window_handles: 86 if handle != original_window: 87 client.switch_to_window(handle) 88 print("Switched to window with '{}' loaded.".format(client.get_url())) 89 client.switch_to_window(original_window) 90 91 Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the 92 context of a new frame (e.g an <iframe> element): 93 94 .. code-block:: python 95 96 iframe = client.find_element(By.TAG_NAME, 'iframe') 97 client.switch_to_frame(iframe) 98 99 Finally Marionette can switch between `chrome` and `content` scope. Chrome is a 100 privileged scope where you can access things like the Firefox UI itself. 101 Content scope is where things like webpages live. You can switch between 102 `chrome` and `content` using the :func:`~Marionette.set_context` and :func:`~Marionette.using_context` functions: 103 104 .. code-block:: python 105 106 client.set_context(client.CONTEXT_CONTENT) 107 # content scope 108 with client.using_context(client.CONTEXT_CHROME): 109 #chrome scope 110 pass # ... do stuff ... 111 # content scope restored 112 113 114 Navigation 115 ---------- 116 117 Use :func:`~Marionette.navigate` to open a new website. It's also possible to 118 move through the back/forward cache using :func:`~Marionette.go_forward` and 119 :func:`~Marionette.go_back` respectively. To retrieve the currently 120 open website, use :func:`~Marionette.get_url`: 121 122 .. code-block:: python 123 124 url = 'http://mozilla.org' 125 client.navigate(url) 126 client.go_back() 127 client.go_forward() 128 assert client.get_url() == url 129 130 131 DOM Elements 132 ------------ 133 134 In order to inspect or manipulate actual DOM elements, they must first be found 135 using the :func:`~Marionette.find_element` or :func:`~Marionette.find_elements` 136 methods: 137 138 .. code-block:: python 139 140 from marionette_driver.marionette import WebElement 141 element = client.find_element(By.ID, 'my-id') 142 assert type(element) == WebElement 143 elements = client.find_elements(By.TAG_NAME, 'a') 144 assert type(elements) == list 145 146 For a full list of valid search strategies, see :doc:`advanced/findelement`. 147 148 Now that an element has been found, it's possible to manipulate it: 149 150 .. code-block:: python 151 152 element.click() 153 element.send_keys('hello!') 154 print(element.get_attribute('style')) 155 156 For the full list of possible commands, see the :class:`WebElement` 157 reference. 158 159 Be warned that a reference to an element object can become stale if it was 160 modified or removed from the document. See :doc:`advanced/stale` for tips 161 on working around this limitation. 162 163 Script Execution 164 ---------------- 165 166 Sometimes Marionette's provided APIs just aren't enough and it is necessary to 167 run arbitrary javascript. This is accomplished with the 168 :func:`~Marionette.execute_script` and :func:`~Marionette.execute_async_script` 169 functions. They accomplish what their names suggest, the former executes some 170 synchronous JavaScript, while the latter provides a callback mechanism for 171 running asynchronous JavaScript: 172 173 .. code-block:: python 174 175 result = client.execute_script("return arguments[0] + arguments[1];", 176 script_args=[2, 3]) 177 assert result == 5 178 179 The async method works the same way, except it won't return until the 180 `resolve()` function is called: 181 182 .. code-block:: python 183 184 result = client.execute_async_script(""" 185 let [resolve] = arguments; 186 setTimeout(function() { 187 resolve("all done"); 188 }, arguments[0]); 189 """, script_args=[1000]) 190 assert result == "all done" 191 192 Beware that running asynchronous scripts can potentially hang the program 193 indefinitely if they are not written properly. It is generally a good idea to 194 set a script timeout using :func:`~Marionette.timeout.script` and handling 195 `ScriptTimeoutException`.