Syn - a Standalone Synthetic Event Library.

Monday 12 July, 2010 by moschel

Syn is used to simulate user actions such as typing, clicking, dragging the mouse.  It creates synthetic events and performs default event behavior.

Features

  • Enables complex JavaScript functional testing.
  • Standalone (does not need jQuery).
  • Cross Browser (IE6+, FF1.5+, Opera 9.6+, Safari 4+, Chrome 4+) and multi-platform (Win, Safari, Linux).
  • 99.9% browser sniffing free (only 1 sniff to prevent crashing Safari).

Download

syn.js (59KB. Size doesn't matter. Users don't download this)

Demo

Synthetic Recorder Demo. If you find a bug, it's the recorder and not syn.js's fault. But please let us know! Eventually, we will turn the recorder into something like the Selenium IDE.

Documentation

JavaScriptMVC's Syn documentation.

Why

Testing rich, dynamic web applications sucks. At Jupiter, we've tried almost every testing solution available (qUnit, Quick Test Pro, Selenium, JsUnit, Env.js, TestCase) and all of them suffer from some fatal flaw.  

Problems:

  • Manual - A tester has to run the tests manually on every supported browser.  People are lazy. 
  • Unit Tests Only - We need to test the app as a whole and complex UI behavior like drag-drop.
  • Low fidelity - We need to make sure the tests are reporting accurate results.
  • Difficult to write - We sling JS like a ninja monkey throws poo.  We want to write tests in a nice JS API.
  • Expensive - A QTP license is 5k a person!  I'd rather buy a vacation.
  • Support - We want to test Mac and Linux browsers.

We've solved all of these problems in our upcoming FuncUnit testing framework. It's a mashup of qUnit, Selenium, Rhino, and Env.js. But its core library, Syn, which does the work of simulating user actions with very high fidelity, is what we are releasing today.

Use

Using Syn feels good and should be done with a clean conscience, as it may lead to feelings of guilty pleasure. There's only several things a user does and these correspond nicely to the Syn API:

  • click - a mousedown and mouseup on the same element.
  • dblclick - two clicks in rapid succession.
  • type - typing a sequence of characters.
  • key - typing a single character
  • move - moving the mouse.
  • drag - a mousedown followed by a move and a mouseup.

These user interactions are called actions. The following is an example of clicking an element with id='hello', typing "Hello World", and then dragging your mouse from that element to an element with id='trash'.

Syn.click( {},'hello' )
   .type( 'Hello World' )
   .drag( $('#trash') ); 

* jQuery is used to get the trash element for brevity. It is not required.

You'll notice that Syn is chainable. You want to use chaining because actions don't complete immediately (thank IE's .focus() for this). Provide a callback to know when a command has completed and use a delay to for timeouts between commands. The following asserts that 'hello' has been removed from the page:

Syn.click( {},'hello' )
   .type( 'Hello World' )
   .delay()         //waits 600ms seconds by default
   .drag( $('#trash') , function() {
     ok( $('#hello').length == 0, "removed hello" )
   }); 

Notice that an element id is absent from the type and drag methods. If an element id is not provided, the previous command's element is used.

Lets explore a how to use each action in detail.

Click and Dblclick

A click is the following sequence of events followed by the event's default actions:

  • mousedown
  • focus (Called only if the element was focusable, and mousedown's default actions were not prevented)
  • mouseup
  • click

A dblclick is just two clicks followed by a dblclick event. The options for a click action become properties on the event. The following sets clientX and clientY for the mousedown and mouseup of a click action:

Syn.click({clientX: 533, clientY: 100}, 'foo')

A few important points on click:

  • ClientX and ClientY are relative to the viewport (window).  
  • You can provide pageX and pageY for positions relative to the page (such as jQuery's offset).
  • If jQuery is available, the clientX and clientY will be set to the center of the element.
  • If you want to set the control key values of the click action, you have to type or key that button.

Type and Key

The key action is the following sequence of events followed by the event's default actions:

  • keydown (Except for the print key)
  • keypress (For keys that generate a keypress)
  • focus (for keys that change focus, like \t)
  • keyup

The type action calls the key action for each character. Check the keycodes documentation for a list of key names. The following types "JavaScriptMVC" then presses backspaces to delete the "MVC", leaving "JavaScript".

Syn.type( 'JavaScriptMVC\b\b\b', 'foo' )

Special Characters

Some characters don't have a type-able key such as right, ctrl, and delete. To type these keys you just have to surround them with [ and ]. The following types JavaScriptMVC, moves the cursor to the left 3 times, and then types delete 3 times:

Syn.type(
 'JavaScriptMVC[left][left][left][delete][delete][delete]',
 'foo')

Command Keys

The command keys ctrl, alt, and shift often want to be held while another key is pressed. For these keys, you have to call the key like [command], and release the key with [command-up]. The following types JavaScriptMVC, selects the MVC text, and then deletes it.

Syn.type(
 'JavaScriptMVC[shift][left][left][left][shift-up]\b',
 'foo')

Drag and Move

A move action is the following events

  • mousemove
  • mouseout (the cursor leaves an element)
  • mouseover (the cursor enters an element)

A move creates a linear sequence of mouse move, outs and overs between two points. A drag action is a mousedown followed by a move and a final mouseup. To make a drag motion from one point to another over 2 seconds:

Syn.drag(
     {
       from: {clientX: 100, clientY: 100},
       to:   {clientX: 200, clientY: 200},
       duration: 2000
     },
     'foo') 

Or if you want to provide pageX and pageY:

Syn.drag(
     {
        from: {pageX: 100, pageY: 100}, 
        to:   {pageX: 200, pageY: 200} 
     }, 
     'foo')

You can shorten this to:

Syn.drag(
     {from: '100x100', to: '200x200' },
     'foo')

But most commonly, you want to drag one element to another element. To drag the foo to the bar element:

Syn.drag(
     document.getElementById('bar'),
     'foo')

This drags from the center of one element to the other.

Conclusion

You should care about syn if you:

  • Work on the Selenium or some other functional testing library.
  • Want to test complex UI behavior.

Our naive hope is that Syn might be the Sizzle of functional testing - a standalone, extendable, drop in, synthetic event library.

Subscribe to:

Related Content