From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- testing/marionette/client/.flake8 | 3 + testing/marionette/client/MANIFEST.in | 2 + testing/marionette/client/docs/Makefile | 153 ++ .../marionette/client/docs/advanced/actions.rst | 46 + testing/marionette/client/docs/advanced/debug.rst | 54 + .../client/docs/advanced/findelement.rst | 126 ++ .../marionette/client/docs/advanced/landing.rst | 13 + testing/marionette/client/docs/advanced/stale.rst | 71 + testing/marionette/client/docs/basics.rst | 185 ++ testing/marionette/client/docs/conf.py | 259 +++ testing/marionette/client/docs/index.rst | 16 + testing/marionette/client/docs/interactive.rst | 55 + testing/marionette/client/docs/make.bat | 190 ++ testing/marionette/client/docs/reference.rst | 51 + .../client/marionette_driver/__init__.py | 26 + .../marionette/client/marionette_driver/addons.py | 70 + testing/marionette/client/marionette_driver/by.py | 27 + .../client/marionette_driver/date_time_value.py | 49 + .../client/marionette_driver/decorators.py | 69 + .../marionette/client/marionette_driver/errors.py | 179 ++ .../client/marionette_driver/expected.py | 311 +++ .../client/marionette_driver/geckoinstance.py | 467 +++++ .../client/marionette_driver/gestures.py | 93 + .../marionette/client/marionette_driver/keys.py | 84 + .../client/marionette_driver/localization.py | 54 + .../client/marionette_driver/marionette.py | 2153 ++++++++++++++++++++ .../client/marionette_driver/selection.py | 227 +++ .../marionette/client/marionette_driver/timeout.py | 98 + .../client/marionette_driver/transport.py | 300 +++ .../marionette/client/marionette_driver/wait.py | 167 ++ testing/marionette/client/requirements.txt | 2 + testing/marionette/client/setup.py | 49 + 32 files changed, 5649 insertions(+) create mode 100644 testing/marionette/client/.flake8 create mode 100644 testing/marionette/client/MANIFEST.in create mode 100644 testing/marionette/client/docs/Makefile create mode 100644 testing/marionette/client/docs/advanced/actions.rst create mode 100644 testing/marionette/client/docs/advanced/debug.rst create mode 100644 testing/marionette/client/docs/advanced/findelement.rst create mode 100644 testing/marionette/client/docs/advanced/landing.rst create mode 100644 testing/marionette/client/docs/advanced/stale.rst create mode 100644 testing/marionette/client/docs/basics.rst create mode 100644 testing/marionette/client/docs/conf.py create mode 100644 testing/marionette/client/docs/index.rst create mode 100644 testing/marionette/client/docs/interactive.rst create mode 100644 testing/marionette/client/docs/make.bat create mode 100644 testing/marionette/client/docs/reference.rst create mode 100644 testing/marionette/client/marionette_driver/__init__.py create mode 100644 testing/marionette/client/marionette_driver/addons.py create mode 100644 testing/marionette/client/marionette_driver/by.py create mode 100644 testing/marionette/client/marionette_driver/date_time_value.py create mode 100644 testing/marionette/client/marionette_driver/decorators.py create mode 100644 testing/marionette/client/marionette_driver/errors.py create mode 100644 testing/marionette/client/marionette_driver/expected.py create mode 100644 testing/marionette/client/marionette_driver/geckoinstance.py create mode 100644 testing/marionette/client/marionette_driver/gestures.py create mode 100644 testing/marionette/client/marionette_driver/keys.py create mode 100644 testing/marionette/client/marionette_driver/localization.py create mode 100644 testing/marionette/client/marionette_driver/marionette.py create mode 100644 testing/marionette/client/marionette_driver/selection.py create mode 100644 testing/marionette/client/marionette_driver/timeout.py create mode 100644 testing/marionette/client/marionette_driver/transport.py create mode 100644 testing/marionette/client/marionette_driver/wait.py create mode 100644 testing/marionette/client/requirements.txt create mode 100644 testing/marionette/client/setup.py (limited to 'testing/marionette/client') diff --git a/testing/marionette/client/.flake8 b/testing/marionette/client/.flake8 new file mode 100644 index 000000000..5f607bc63 --- /dev/null +++ b/testing/marionette/client/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 99 +exclude = __init__.py,disti/*,build/*, diff --git a/testing/marionette/client/MANIFEST.in b/testing/marionette/client/MANIFEST.in new file mode 100644 index 000000000..cf628b039 --- /dev/null +++ b/testing/marionette/client/MANIFEST.in @@ -0,0 +1,2 @@ +exclude MANIFEST.in +include requirements.txt diff --git a/testing/marionette/client/docs/Makefile b/testing/marionette/client/docs/Makefile new file mode 100644 index 000000000..f3d89d6d4 --- /dev/null +++ b/testing/marionette/client/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MarionettePythonClient.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MarionettePythonClient.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/MarionettePythonClient" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MarionettePythonClient" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/testing/marionette/client/docs/advanced/actions.rst b/testing/marionette/client/docs/advanced/actions.rst new file mode 100644 index 000000000..294855a6f --- /dev/null +++ b/testing/marionette/client/docs/advanced/actions.rst @@ -0,0 +1,46 @@ +Actions +======= + +.. py:currentmodule:: marionette + +Action Sequences +---------------- + +:class:`Actions` are designed as a way to simulate user input as closely as possible +on a touch device like a smart phone. A common operation is to tap the screen +and drag your finger to another part of the screen and lift it off. + +This can be simulated using an Action:: + + from marionette import Actions + + start_element = marionette.find_element('id', 'start') + end_element = marionette.find_element('id', 'end') + + action = Actions(marionette) + action.press(start_element).wait(1).move(end_element).release() + action.perform() + +This will simulate pressing an element, waiting for one second, moving the +finger over to another element and then lifting the finger off the screen. The +wait is optional in this case, but can be useful for simulating delays typical +to a users behaviour. + +Multi-Action Sequences +---------------------- + +Sometimes it may be necessary to simulate multiple actions at the same time. +For example a user may be dragging one finger while tapping another. This is +where :class:`MultiActions` come in. MultiActions are simply a way of combining +two or more actions together and performing them all at the same time:: + + action1 = Actions(marionette) + action1.press(start_element).move(end_element).release() + + action2 = Actions(marionette) + action2.press(another_element).wait(1).release() + + multi = MultiActions(marionette) + multi.add(action1) + multi.add(action2) + multi.perform() diff --git a/testing/marionette/client/docs/advanced/debug.rst b/testing/marionette/client/docs/advanced/debug.rst new file mode 100644 index 000000000..e72d2549b --- /dev/null +++ b/testing/marionette/client/docs/advanced/debug.rst @@ -0,0 +1,54 @@ +Debugging +========= + +.. py:currentmodule:: marionette + +Sometimes when working with Marionette you'll run into unexpected behaviour and +need to do some debugging. This page outlines some of the Marionette methods +that can be useful to you. + +Please note that the best tools for debugging are the `ones that ship with +Gecko`_. This page doesn't describe how to use those with Marionette. Also see +a related topic about `using the debugger with Marionette`_ on MDN. + +.. _ones that ship with Gecko: https://developer.mozilla.org/en-US/docs/Tools +.. _using the debugger with Marionette: https://developer.mozilla.org/en-US/docs/Marionette/Debugging + + +Storing Logs on the Server +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By calling `~Marionette.log` it is possible to store a message on the server. +Logs can later be retrieved using `~Marionette.get_logs`. For example:: + + try: + marionette.log("Sending a click event") # logged at INFO level + elem.click() + except: + marionette.log("Something went wrong!", "ERROR") + + print(marionette.get_logs()) + +Disclaimer: Example for illustrative purposes only, don't actually hide +tracebacks like that! + + +Seeing What's on the Page +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes it's difficult to tell what is actually on the page that is being +manipulated. Either because it happens too fast, the window isn't big enough or +you are manipulating a remote server! There are two methods that can help you +out. The first is `~Marionette.screenshot`:: + + marionette.screenshot() # takes screenshot of entire frame + elem = marionette.find_element(By.ID, 'some-div') + marionette.screenshot(elem) # takes a screenshot of only the given element + +Sometimes you just want to see the DOM layout. You can do this with the +`~Marionette.page_source` property. Note that the page source depends on the +context you are in:: + + print(marionette.page_source) + marionette.set_context('chrome') + print(marionette.page_source) diff --git a/testing/marionette/client/docs/advanced/findelement.rst b/testing/marionette/client/docs/advanced/findelement.rst new file mode 100644 index 000000000..abcbc8e89 --- /dev/null +++ b/testing/marionette/client/docs/advanced/findelement.rst @@ -0,0 +1,126 @@ +Finding Elements +================ +.. py:currentmodule:: marionette + +One of the most common and yet often most difficult tasks in Marionette is +finding a DOM element on a webpage or in the chrome UI. Marionette provides +several different search strategies to use when finding elements. All search +strategies work with both :func:`~Marionette.find_element` and +:func:`~Marionette.find_elements`, though some strategies are not implemented +in chrome scope. + +In the event that more than one element is matched by the query, +:func:`~Marionette.find_element` will only return the first element found. In +the event that no elements are matched by the query, +:func:`~Marionette.find_element` will raise `NoSuchElementException` while +:func:`~Marionette.find_elements` will return an empty list. + +Search Strategies +----------------- + +Search strategies are defined in the :class:`By` class:: + + from marionette import By + print(By.ID) + +The strategies are: + +* `id` - The easiest way to find an element is to refer to its id directly:: + + container = client.find_element(By.ID, 'container') + +* `class name` - To find elements belonging to a certain class, use `class name`:: + + buttons = client.find_elements(By.CLASS_NAME, 'button') + +* `css selector` - It's also possible to find elements using a `css selector`_:: + + container_buttons = client.find_elements(By.CSS_SELECTOR, '#container .buttons') + +* `name` - Find elements by their name attribute (not implemented in chrome + scope):: + + form = client.find_element(By.NAME, 'signup') + +* `tag name` - To find all the elements with a given tag, use `tag name`:: + + paragraphs = client.find_elements(By.TAG_NAME, 'p') + +* `link text` - A convenience strategy for finding link elements by their + innerHTML (not implemented in chrome scope):: + + link = client.find_element(By.LINK_TEXT, 'Click me!') + +* `partial link text` - Same as `link text` except substrings of the innerHTML + are matched (not implemented in chrome scope):: + + link = client.find_element(By.PARTIAL_LINK_TEXT, 'Clic') + +* `xpath` - Find elements using an xpath_ query:: + + elem = client.find_element(By.XPATH, './/*[@id="foobar"') + +.. _css selector: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors +.. _xpath: https://developer.mozilla.org/en-US/docs/Web/XPath + + + +Chaining Searches +----------------- + +In addition to the methods on the Marionette object, HTMLElement objects also +provide :func:`~HTMLElement.find_element` and :func:`~HTMLElement.find_elements` +methods. The difference is that only child nodes of the element will be searched. +Consider the following html snippet:: + +
+ +
+ + +Doing the following will work:: + + client.find_element(By.ID, 'container').find_element(By.ID, 'main') + +But this will raise a `NoSuchElementException`:: + + client.find_element(By.ID, 'container').find_element(By.ID, 'footer') + + +Finding Anonymous Nodes +----------------------- + +When working in chrome scope, for example manipulating the Firefox user +interface, you may run into something called an anonymous node. + +Firefox uses a markup language called XUL_ for its interface. XUL is similar +to HTML in that it has a DOM and tags that render controls on the display. One +ability of XUL is to create re-useable widgets that are made up out of several +smaller XUL elements. These widgets can be bound to the DOM using something +called the `XML binding language (XBL)`_. + +The end result is that the DOM sees the widget as a single entity. It doesn't +know anything about how that widget is made up. All of the smaller XUL elements +that make up the widget are called `anonymous content`_. It is not possible to +query such elements using traditional DOM methods like `getElementById`. + +Marionette provides two special strategies used for finding anonymous content. +Unlike normal elements, anonymous nodes can only be seen by their parent. So +it's necessary to first find the parent element and then search for the +anonymous children from there. + +* `anon` - Finds all anonymous children of the element, there is no search term + so `None` must be passed in:: + + anon_children = client.find_element('id', 'parent').find_elements('anon', None) + +* `anon attribute` - Find an anonymous child based on an attribute. An + unofficial convention is for anonymous nodes to have an + `anonid` attribute:: + + anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'}) + + +.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL +.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL +.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content diff --git a/testing/marionette/client/docs/advanced/landing.rst b/testing/marionette/client/docs/advanced/landing.rst new file mode 100644 index 000000000..0a44de63d --- /dev/null +++ b/testing/marionette/client/docs/advanced/landing.rst @@ -0,0 +1,13 @@ +Advanced Topics +=============== + +Here are a collection of articles explaining some of the more complicated +aspects of Marionette. + +.. toctree:: + :maxdepth: 1 + + findelement + stale + actions + debug diff --git a/testing/marionette/client/docs/advanced/stale.rst b/testing/marionette/client/docs/advanced/stale.rst new file mode 100644 index 000000000..0af576865 --- /dev/null +++ b/testing/marionette/client/docs/advanced/stale.rst @@ -0,0 +1,71 @@ +Dealing with Stale Elements +=========================== +.. py:currentmodule:: marionette + +Marionette does not keep a live representation of the DOM saved. All it can do +is send commands to the Marionette server which queries the DOM on the client's +behalf. References to elements are also not passed from server to client. A +unique id is generated for each element that gets referenced and a mapping of +id to element object is stored on the server. When commands such as +:func:`~HTMLElement.click()` are run, the client sends the element's id along +with the command. The server looks up the proper DOM element in its reference +table and executes the command on it. + +In practice this means that the DOM can change state and Marionette will never +know until it sends another query. For example, look at the following HTML:: + + + + + + +
+
+ + + +Care needs to be taken as the DOM is being modified after the page has loaded. +The following code has a race condition:: + + button = client.find_element('id', 'button') + button.click() + assert len(client.find_elements('css selector', '#container div')) > 0 + + +Explicit Waiting and Expected Conditions +---------------------------------------- + +To avoid the above scenario, manual synchronisation is needed. Waits are used +to pause program execution until a given condition is true. This is a useful +technique to employ when documents load new content or change after +``Document.readyState``'s value changes to "complete". + +The :class:`Wait` helper class provided by Marionette avoids some of the +caveats of ``time.sleep(n)``. It will return immediately once the provided +condition evaluates to true. + +To avoid the race condition in the above example, one could do:: + + button = client.find_element('id', 'button') + button.click() + + def find_divs(): + return client.find_elements('css selector', '#container div') + + divs = Wait(client).until(find_divs) + assert len(divs) > 0 + +This avoids the race condition. Because finding elements is a common condition +to wait for, it is built in to Marionette. Instead of the above, you could +write:: + + button = client.find_element('id', 'button') + button.click() + assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0 + +For a full list of built-in conditions, see :mod:`~marionette.expected`. diff --git a/testing/marionette/client/docs/basics.rst b/testing/marionette/client/docs/basics.rst new file mode 100644 index 000000000..0de72c625 --- /dev/null +++ b/testing/marionette/client/docs/basics.rst @@ -0,0 +1,185 @@ +.. py:currentmodule:: marionette_driver.marionette + +Marionette Python Client +======================== + +The Marionette Python client library allows you to remotely control a +Gecko-based browser or device which is running a Marionette_ +server. This includes Firefox Desktop and Firefox for Android. + +The Marionette server is built directly into Gecko and can be started by +passing in a command line option to Gecko, or by using a Marionette-enabled +build. The server listens for connections from various clients. Clients can +then control Gecko by sending commands to the server. + +This is the official Python client for Marionette. There also exists a +`NodeJS client`_ maintained by the Firefox OS automation team. + +.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette +.. _NodeJS client: https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette + +Getting the Client +------------------ + +The Python client is officially supported. To install it, first make sure you +have `pip installed`_ then run: + +.. parsed-literal:: + pip install marionette_driver + +It's highly recommended to use virtualenv_ when installing Marionette to avoid +package conflicts and other general nastiness. + +You should now be ready to start using Marionette. The best way to learn is to +play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up +a python shell and follow along with the +:doc:`interactive tutorial `! + +.. _pip installed: https://pip.pypa.io/en/latest/installing.html +.. _virtualenv: http://virtualenv.readthedocs.org/en/latest/ +.. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds + +Using the Client for Testing +---------------------------- + +Please visit the `Marionette Tests`_ section on MDN for information regarding +testing with Marionette. + +.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests + +Session Management +------------------ +A session is a single instance of a Marionette client connected to a Marionette +server. Before you can start executing commands, you need to start a session +with :func:`start_session() `: + +.. parsed-literal:: + from marionette_driver.marionette import Marionette + + client = Marionette('localhost', port=2828) + client.start_session() + +This returns a session id and an object listing the capabilities of the +Marionette server. For example, a server running on Firefox Desktop will +have some features which a server running from Firefox Android won't. +It's also possible to access the capabilities using the +:attr:`~Marionette.session_capabilities` attribute. After finishing with a +session, you can delete it with :func:`~Marionette.delete_session()`. Note that +this will also happen automatically when the Marionette object is garbage +collected. + +Context Management +------------------ +Commands can only be executed in a single window, frame and scope at a time. In +order to run commands elsewhere, it's necessary to explicitly switch to the +appropriate context. + +Use :func:`~Marionette.switch_to_window` to execute commands in the context of a +new window: + +.. parsed-literal:: + original_window = client.current_window_handle + for handle in client.window_handles: + if handle != original_window: + client.switch_to_window(handle) + print("Switched to window with '{}' loaded.".format(client.get_url())) + client.switch_to_window(original_window) + +Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the +context of a new frame (e.g an