{X} HISTORICAL.
We no longer use windmill at all. For Javascript XHR tests, see standard_yuixhr_test_template.js and standard_yuixhr_test_template.py in the root of the Launchpad tree.

This page provides information about writing JavaScript integration tests.

Windmill for JS Integration Tests

For integration testing that covers JS workflows, our tool of choice is Windmill.

Using Windmill to Test Launchpad

Setting up Windmill

Windmill is included in the Launchpad source tree. Its dependencies are installed by the launchpad-developer-dependencies ubuntu package.

Running the Launchpad Windmill test suite

To run all Windmill tests:

To run all Windmill tests for a single application (e.g. the Bugs application):

To run a single Windmill test (e.g. test_bug_commenting() in the Bugs application):

To run Windmill test in headless mode (without opening a Firefox window)

Here is a handy bash alias you can use to run tests from anywhere in the tree:

The Windmill tests are integrated into our normal test suite, and the tests has a layered named FooWindmillLayer, where Foo is the name of the application (e.g. BugsWindmillLayer. The Windmill layers are excluded by default, so when running a Windmill tests you always have to specify the --layer option, otherwise the test runner won't find it.

How are the tests organized

Tests written in python are placed in lib/lp/<application>/windmill/tests.

Tests using the JS API are in lib/canonical/launchpad/windmill/jstests.

Both are described in the Windmill Wiki chapter on Authoring Tests.

Test Writing Tips

When using open(), make sure to use waits.forPageLoad() before any waits.forElement() (unless you are absolutely sure that you can't be on a page that already contains that element).

When using the lazr-js slide-in/slide-out effects, use the following assertions to test whether the element is visible or not.

    # Test for visibility.
    client.asserts.assertProperty(
        id=u'search-results',
        validator='className|lazr-opened')

    # Test for invisibility.
    client.asserts.assertProperty(
        id=u'search-results',
        validator='className|lazr-closed')

When hiding or revealing elements using POJS, use the unseen class to control this behavior. It's easy to test for too. E.g.

    // Hide this element.
    element1.addClass('unseen');
    // Reveal this element.
    element2.removeClass('unseen');

    # Test for visibility.
    client.asserts.assertNotProperty(
        id=u'search-results',
        validator='className|unseen')

    # Test for invisibility.
    client.asserts.assertProperty(
        id=u'search-results',
        validator='className|unseen')

For writing test containing conditions based on the page content you can use the execJS method.

        code = (
            "lookupNode({id: '%s'}).text == 'Working in reviewer mode.'" %
            self.current_working_mode)
        current_is_reviewer = self.client.execJS(js=code)['output']
        if current_is_reviewer:
            do_something()

Known problems

Logging in before opening a page might not really log you in

If you log in using ensure_login() before you open a page, you might not actually get logged in.

Workaround: Open the page you want to test before logging in.

Bug: #487657

Asserting that a node exists directly after waiting for it sometimes fails

If you wait for a node, and then directly assert that it's there, you sometimes get a failure when doing the assert, claiming the node isn't there. Example code:

    client.waits.forElement(xpath=ADD_COMMENT_BUTTON)
    client.asserts.assertNode (xpath=ADD_COMMENT_BUTTON)

Workaround: Don't use assertNode() after forElement(). Waiting for the element already asserts that the node is there, and there's no need checking it again.

Bug: #487666

External resources

Troubleshooting

  File "/usr/lib/python2.4/urllib.py", line 82, in urlopen
    return opener.open(url)
  File "/usr/lib/python2.4/urllib.py", line 190, in open
    return getattr(self, name)(url)
  File "/usr/lib/python2.4/urllib.py", line 316, in open_http
    errcode, errmsg, headers = h.getreply()
  File "/usr/lib/python2.4/httplib.py", line 1137, in getreply
    response = self._conn.getresponse()
  File "/usr/lib/python2.4/httplib.py", line 866, in getresponse
    response.begin()
  File "/usr/lib/python2.4/httplib.py", line 336, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python2.4/httplib.py", line 294, in _read_status
    line = self.fp.readline()
  File "/usr/lib/python2.4/socket.py", line 317, in readline
    data = recv(1)
IOError: [Errno socket error] (104, 'Connection reset by peer')


CategoryJavaScript CategoryTesting

Windmill (last edited 2011-09-23 13:49:05 by gary)