2013-01-31

Headless Browser Testing: Dart, DumpRenderTree, drone.io

When writing any software, even for the web, I try to do as much testing as possible outside the browser.

A lot of great testing can be done of algorithms, data models, and business logic without booting up Chrome.

Node.js enabled this with Javascript. Dart has had an out-of-browser--read console--story since day one.

But some things you must test in a browser.

The Dart Bag of Tricks (BOT) has had browser tests since the beginning. There are only a few tests that must be run in the browser, but I make sure to run all tests both on the console and in the browser if they can run both places.

Browser tests are great, but they don't fit well into a build workflow or a continuous integration tool.

For that you need a way to run and control a browser (or a browser-like thing) via the console and get results out. Enter DumpRenderTree.

DumpRenderTree - Chrome without the chrome

DumpRenderTree (DRT) is a great little tool hidden in the guts of WebKit.

By default, DRT prints out an obscure text format representing the hierarchy of elements on the provided page.

Here's the output for DumpRenderTree example/fract/fract_demo.html

Content-Type: text/plain
layer at (0,0) size 808x820
  RenderView at (0,0) size 800x600
layer at (0,0) size 800x820
  RenderBlock {HTML} at (0,0) size 800x820
    RenderBody {BODY} at (8,8) size 784x804
      RenderHTMLCanvas {CANVAS} at (0,0) size 800x800 [bgcolor=#808080]
      RenderText {#text} at (0,0) size 0x0
#EOF
#EOF

DRT is controlled mostly through Javascript in the test page by via window.testRunner.

Here's the script block that lives in test/harness_browser.html:

<script>
  // only run if testRunner is defined -- we're in DRT
  if (window.testRunner) {

    // Don't dump the structure. Just the text of the output plus console output
    testRunner.dumpAsText();

    // Don't finish when layout is done. Instead, wait to be notified
    testRunner.waitUntilDone();

    // listen for messages from the test harness
    window.addEventListener("message", receiveMessage, false);

    // listen for unhandled exceptions
    window.addEventListener('error', onError);
  }

  // if there is an unhandled exception, tell DRT we're done
  function onError(event) {
    testRunner.notifyDone();
  }

  // if the test harness sends a done message, tell DRT we're done
  function receiveMessage(event) {
    if(event.data == 'unittest-suite-done') {
      testRunner.notifyDone();
    }
  }
</script>

And since everything is keyed off the existence of window.testRunner, none of this affects the behavior of tests running normally in a browser.

Here's the first few lines of output from DumpRenderTree test/harness_browser.html:

Content-Type: text/plain
PASS
All 109 tests passed
Collapse All
bot
 
(4/4)
bot - Enumerable
 
(19/19)
bot - Enumerable - group

Compare to the content running the tests in the browser:


Getting DumpRenderTree

DRT is part of Linux and Windows builds of the Dart Editor now (look in the chromium) directory. It will be part of builds for Mac soon.

If you want to get DRT that is compatible with the latest integration build of Dart - 0.3.2.0 (r17657) - grab this guy.

Just make sure the DumpRenderTree executable is on your path when running tests.

Bringing it all together

drone.io supports DumpRenderTree for Dart projects. Read about it here.

The trick is creating a script that returns an exit code of zero for success and not zero (1 is pretty standard) for failure.

Check out the script I created for BOT.

And let me know how it goes for you.

Happy hacking.