Mozilla Marionette & Firefox UI Tests

Created by Sanyam Khurana | @ErSanyamKhurana | @CuriousLearner

Who am I?

  • One of YOU! -- Part of the community
  • Mozilla Rep
  • Open Source Enthusiast
  • Also known as "CuriousLearner"

What is Marionette?

  • Automation driver for Mozilla's Gecko Engine
  • Control either UI/JS of Gecko platform such as Firefox
  • Controls both Chrome and Content
  • Ability to replicate user actions
  • Among similar lines as Selenium

How does it work?

Marionette consists of two parts:

  • A server which takes requests and executes them in Gecko
  • The client sends commands to the server and the server executes the command inside the browser.

But before we begin...

Let's learn a little bit about mach

Mach (German for do) is a command-line interface to help developers perform common tasks.

You build, run, test your builds using mach

... and much more ...

Let's install Marionette

pip install marionette_client

Run Marionette enabled build

./mach run -marionette

Connect our client to marionette enabled build

from marionette import Marionette
client = Marionette('localhost', port=2828)

Session Management

client.start_session()
client.local_port
client.session_capabilities
client.delete_session()

Context Management

CONTENT CONTEXT

client.CONTEXT_CONTENT

CHROME CONTEXT

client.CONTEXT_CHROME

Set CONTEXT using set_context method

client.set_context(client.CONTEXT_CONTENT)

Navigation

url = 'http://mozilla.org'
client.navigate(url)
url = 'http://SanyamKhurana.com'
client.navigate(url)
client.go_back()
client.go_forward()
assert client.get_url() == url

Manipulating DOM elements

Two methods to traverse and find element in DOM
find_element()
find_elements()
Let's play with it
from marionette_driver.marionette import HTMLElement
element = client.find_element(By.ID, 'first_name')
assert type(element) == HTMLElement
elements = client.find_elements(By.TAG_NAME, 'a')
assert type(elements) == list
element.send_keys('I love Python!')
element.click()
element.get_attribute()

Selecting with By

class name - find element having certain class:
buttons = client.find_elements(By.CLASS_NAME, 'button')
css selector - find element using a css selector:
container_buttons = client.find_elements(By.CSS_SELECTOR, '#container li')
name - find elemnts by name attribute (Not in CHROME scope):
form = client.find_element(By.NAME, 'register')
tag name - find elements with given tag name:
paragraphs = client.find_elements(By.TAG_NAME, 'p')
link text - find link elements by their innerHTML (not in CHROME scope):
link = client.find_element(By.LINK_TEXT, 'I love Python!')
partial link text - Same as link text except substrings of the innerHTML are matched (not in chrome scope):
link = client.find_element(By.PARTIAL_LINK_TEXT, 'love')
xpath - find elements using an xpath query:
elem = client.find_element(By.XPATH, './/*[@id="first-name"')

Execute JS

                        
                            result = client.execute_script("return arguments[0] * arguments[1];",
            script_args=[5, 7])
                        
                    

Use Async JS function

                        
                            result = client.execute_async_script("""
            setTimeout(function() {
            marionetteScriptFinished("finished");
            }, arguments[0]);
            """, script_args=[2000])
assert result == "finished"
                    
                    

                        alert = self.marionette.switch_to_alert()
text = alert.text
alert.accept()
                    

Actions & Multi-Actions


first_element = client.find_element(By.ID, "first_element")
second_element = client.find_element(By.ID, "second_element")
# create action object
action = Actions(client)
# add actions (press, wait, move, release) into the object
action.press(first_element).wait(10).move(second_element).release()
# fire all the added events
action.perform()
                    

                        # create multiaction object
multitouch = MultiActions(client)
# create several action objects
action_1 = Actions(client)
action_2 = Actions(client)
# add actions to each action object/finger
action_1.press(element1).move_to(element2).release()
action_2.press(element3).wait().release(element3)
# fire all the added events
multitouch.add(action_1).add(action_2).perform()
                    

                        # Wait 30 seconds for window to open, checking for its presence once
# every 5 seconds.
wait = Wait(client, timeout=30, interval=5,
ignored_exceptions=errors.NoSuchWindowException)
window = wait.until(lambda m: m.switch_to_window(42))
                    

Where to go from here?

THE END

Questions?


Shout out on Twitter:
@ErSanyamKhurana | #MozCode
Shoot a mail at:
Sanyam@MozPacers.org | Sanyam@SanyamKhurana.com


My websites:
www.SanyamKhurana.com | www.TheGeekyWay.com