Workflow Testing with Casperjs


What is casperjs?

CasperJS is a navigation scripting & testing utility 
written in Javascript that gives you more time to do this 
 
while spending less time doing this
 

What can I do with it?


  • defining & ordering browsing navigation steps
  • filling & submitting forms
  • clicking & following links
  • capturing screenshots of a page (or part of it)
  • testing remote DOM
  • logging events
  • downloading resources, including binary ones
  • writing functional test suites, saving results as JUnit XML
  • scraping Web contents

how we're using it here (so far)


  • Basic Janrain Login test
    (media/web/common/javascript/tests/workflow/login.js)

  • Premium Story Anon/Auth'd test (media/web/common/javascript/tests/workflow/premium-story-login.js)

  • casper.cmg: Includible Helpers

Basic Janrain Login test


  • Opens the specified site's front page (default: test.myajc.com)
  • Waits for Janrain to initialize.
  • Confirms that the UI is consistent with an anonymous viewer
    (.cmUserAnonymous is visible)
  • Tests that login form displays when asked for
  • Tests that the form fills properly
    (default: cmg.medhini+goldsmith@gmail.com)
  • Tests that the UI changes to logged in state.
    (.cmUserAuthed is visible)
  • Logs out

Premium Story Anon/Auth'd test

  • Retrieve the title and URL for a premium story via API
  • Opens story and confirms that the title is as expected
  • Waits for Janrain to init
  • Confirms UI is consistent with anon state
    (same as login.js + story is stubbed, invite block visible, no comments)
  • Test that login form displays when asked for
  • Test that form fills properly
  • Tests that the UI changes to logged in state.
    (.cmUserAuthed visible, story unstubs, invite block invisible, comments loaded)

casper.cmg: INCLUDIBLE helpers


  • You can require them like so:
     var cmg = require('helpers/core');
  • Or by passing --includes=/path/to/tests/workflow/helpers/core.js  will give you access to several helpers.

  • To initialize them just pass the CLI param above and call
    // If you used --includes you'll need
    // var cmg = casper.cmg;
    cmg.init(casper);

CMG Helpers

  • on:
    • simple event handling: pass event name (currently only 'anonymous' and 'logged_in' are supported)
    • allows additional tests/behavior steps to be added before or after logging in

  • testJanrainReady: wait for Janrain to be ready before continuing

  • testUserIsAnonymous:
    • free & premium site compatible test that user is anonymous
    • fires 'anonymous' callbacks for additional test's that the appropriate anonymous UI is displayed

CMG HELPERS (CONT.)

  • testSignInFormDisplayed and testSignInFormFilled:
    test that the UI behaves correctly and allow logging into a site via Janrain

  • testLoginComplete:
    • submits login form and waits for the login process to complete
    • fires any registered callbacks for the 'logged_in' event
       
  • logOut: log out current Janrain user and call casper.test.done unless false is passed

  • testLoginProcess: combines all of the listed helpers above
     casper.cmg.testLoginProcess(casper, user [optional], pass [optional], call_done [optional, default true])

Surviving asynchronicity


  • If your test requires certain Javascript to be loaded/initialized, use waitFor:
    casper.waitFor(function test() {
        return window.janrain && janrain.ready;
    }, function then() {
        test.assertEval(…);
    }/*, function onTimeout, int timeout, obj timeout details*/);
  • If your test requires that a certain dynamically loaded/created element exists, use waitForSelector:
    casper.waitForSelector('.mySelector', function then() {
        test.assertExists(…);
    }/*, function onTimeout, int timeout, obj timeout details*/);
  • REMEMBER: Everything you do should be inside a casper.then or casper.waitFor* call

Tests

  • Test suites's follow this structure:
    casper.test.begin(str description, int expectedTests, function theTests(test) {
        casper.start('http://my.url.tld/path/to/page/', function then(){});
        casper.then(function () { /* do testy stuff */ });
        casper.waitFor(function () { /* do testy stuff */ });
        casper.waitForSelector(function () { /* do testy stuff */ });
        // After all of the tests
        casper.then(function () {
            test.done();
        });
        // This is what makes casper actually start doing stuff.
        casper.run();
    });

Don't assert before you evaluate

  • The test object knows nothing of the DOM.
  • The casper object knows nothing of the DOM.
  • Therefore, you must evaluate your code in the browser context.
  • Here's what I recommend for more complicated tests:
    // for all tests that need to run in page environment 
    test.assertEval(function () { …test code… }, str description)
    
    // for all non-test code that needs to be run in page environment
    this.evaluate(function () { …code to run in browser… })
  • There are a number of shortcut methods that hook into the browser environment: assertVisible, assertDoesntExist, assertElementCount, assertExists, assertTextExists, etc.

GOTCHAS & TIPS

  • Sometimes the waitFor* will time out due to slow site responses. Re-run  the test with a higher --timeout before panicking.
  • utils is your friend:
    // from evaluate/assertEval
    __utils__.method()
    
    // from top script level
    require('utils').method()

  • CLI options:
    • any --name option passed at the command line is available at casper.cli.options.name (or cmg.options, if you're using it)
    • options already used by core helpers: --site, --server, --user,  --pass, --timeout

For more info:


Intro to CasperJS

By Aaron McCall

Intro to CasperJS

  • 2,264