Part 4: Re-using commands to set up Firefox OS

In Parts 2 and 3 we got a working test, but if we wanted to reset its state (for example killing open apps) before running the test, we'd have to do this manually. That’s a bit tedious, so again we should automate it! In this part, we’ll look how to do this and more by breaking out bits of code into separate Python methods that we can re-use.

Automatically resetting state

At the start of a typical test run, we'll probably want to unlock the Firefox OS lock screen and kill all running apps. Let's look at how to do this now.

Unlocking the lock screen

Before we go any further, enable the lock screen again with Settings App > Screen lock > Lock screen, if you have not done so already.

Add the following Python method to your test_add_contact.py file, just inside the class:

def unlock_screen(self):
    self.marionette.execute_script('window.wrappedJSObject.lockScreen.unlock();')

This method will now unlock Firefox OS when called. Now let's call it in our test by adding the following lines below the self.marionette.start_session() line:

# Unlock the screen
self.unlock_screen()

Killing any open apps

Now we'll add a method into our code to kill all open apps when run. This looks like so:

    def kill_all(self):
        self.marionette.switch_to_frame()
        self.marionette.execute_async_script("""
             // Kills all running apps, except the homescreen.
             function killAll() {
               let manager = window.wrappedJSObject.appWindowManager;

               let apps = manager.getApps();
               for (let id in apps) {
                 let origin = apps[id].origin;
                 if (origin.indexOf('verticalhome') == -1) {
                   manager.kill(origin);
                 }
               }
             };
             killAll();
             // return true so execute_async_script knows the script is complete
             marionetteScriptFinished(true);
            """)

Add this just after the unlock_screen method we added in the last section.

Next, add the following to run this along with the rest of the test; add it just after the self.unlock_screen() line:

# kill all open apps
self.kill_all()

Now try leaving the contacts app open after the last time the test was run and returning to the lock screen before trying to run the test again. In addition to the screen being unlocked, the open Contacts app will be automatically killed before the test is rerun, so its state won’t affect the test you’re running now. This is important for the long term reliability of your test run.

Run the test again a few times and see if everything works and Firefox OS is reset properly.

Dynamic waits

In Part 3 we mentioned the importance of dynamic waits. Marionette has waits just like WebDriver/Selenium2, with typical syntax that looks like this:

from marionette_driver import Wait
 
# Wait until element is displayed
Wait(self.marionette).until(lambda m: m.find_element('id', 'element_id').is_displayed())

This code will wait until the specified element is displayed. At that point we know we are ready to interact with it. Let's try using this code construct in our test.

First of all, include the Wait import line, just after your existing import lines:

from marionette_driver import Wait

Now, we can replace the second time.sleep(2) function after tapping the Contacts icon (just after the self.marionette.switch_to_frame() line) with a Wait() method that will wait until the Contacts frame has been displayed:

Wait(self.marionette).until(lambda m: m.find_element('css selector', "iframe[data-url*='contacts']").is_displayed())

When we tap the + symbol to start creating a new contact we want to wait for the Add contact form to have fully slid in to view. The Done (save) button is the next thing we need to tap so we'll wait for that to have slid into position before we continue. Replace the third time.sleep(2) function with the following line:

Wait(self.marionette).until(lambda m: m.find_element('id', 'save-button').location['y']== 0)

In this example we wait for the Done button to get to the top of the screen; the element will be in view at multiple points when it is animating, but its final resting position is the safest thing to wait for.

We can also wait for elements *not* to be displayed. After tapping Done , we'll wait for the Done button to be hidden using a similar Wait() method along with not, before we run the rest of the code. Replace the fourth and final time.sleep(2) function with the following:

Wait(self.marionette).until(lambda m: not m.find_element('id', 'save-button').is_displayed())

If your test is working ok, then great! We've make our test more modular and reliable. In Part 5 we'll introduce you to using a test runner to execute tests.

Document Tags and Contributors

Contributors to this page: chrisdavidmills, noahchampoux, gmealer, __Vinz__, zac_c, Tingyu, yue.Zhou
Last updated by: chrisdavidmills,
Hide Sidebar