Detecting Selenium

I’ve been writing Selenium tests lately. It’s not fun, but it’s helped uncover a few bugs in our app. There are a number of places where Selenium doesn’t shine: its CSS selector engine doesn’t always find the node I’m looking for, server-based tests have a relatively anemic API, and it doesn’t support native drag & drop.

I don’t really agree with PPK’s assessment of HTML5’s drag & drop API: it’s messy, but not impossible. I added support for HTML5 Drag & Drop to the Dashcode/Coherent library with little or no pain; I also added fallback support for Firefox 3.

By manually disabling the support for native drag & drop, all my Selenium tests pass, but that’s not really a scalable solution. So I really needed to find some way to detect that I’m running within the Selenium test harness. Unfortunately, the library installs event handlers immediately, so I need to be able to detect whether Selenium is running before it injects it’s alert handler (which creates the detectable window.seleniumAlert function).

The only thing I’ve found is that Selenium creates my window with the name selenium_main_window or something similar to that. Therefore, I’ve added the following to the Browser flags for Dashcode/Coherent:

SeleniumActive: (-1!==window.name.indexOf('selenium'))

And then in the Support flags I have:

/** Determine whether the browser supports Drag & Drop properly. Mozilla
    prior to 3.5 doesn't work correctly. When running under Selenium, native
    drag and drop is disabled.
 */
DragAndDrop: !coherent.Browser.SeleniumActive &&
             ((coherent.Browser.Safari && !coherent.Browser.MobileSafari) ||
               coherent.Browser.IE ||
              (coherent.Browser.Mozilla && !!window.localStorage))

Of course, this will completely fail if the window is opened via another method, but it’s the best I could do.

Distilation

Like most build systems, Distil requires a build file. This is similar to a build.xml file from Ant[^xml] or a rake file, however, Distil uses a YAML file rather than XML or Ruby. This means its files are simple and easier to understand. This is in keeping with the goal to keep Distil simple and easy to understand.

Distil is not a general purpose build system. Its only purpose in life is to create deployment versions of your Javascript and CSS files and their associated asset files.

The Distil project should be named distil.yml and be placed in the root of your project. If you don’t want a different name for your project file, you’ll need to invoke distil with the -buildfile option like this:

distil -buildfile=project.yml

You can also abbreviate -buildfile as -file or simply -f.

An Example Distil File

The following is a very simple distil project file. It has two targets: all and login.

name: sample
version: 1.0
notice: src/NOTICE

all:
    - src/file1.js
    - src/file2.js
    - src/dir1
    - src/dir2

login:
    include:
        - src/dir3
    exclude:
        - src/dir3/i-hate-this-file.js

Assuming there are only Javascript files in this project, when built distil will generate the following files:

build/sample-debug.js
build/sample-uncompressed.js
build/sample.js
build/sample.js.gz
build/sample-login-debug.js
build/sample-login-uncompressed.js
build/sample-login.js
build/sample-login.js.gz

Additionally, when built in debug mode (the default), distil will create a symbolic link in the build folder to the src folder. This means you can simply serve up the build folder via Apache and everything will just work. When build in release mode, distil will actually copy the source and asset files.

Any top-level key/value pair that isn’t an option is a target. While YAML will permit you to define your options anywhere in the file, you’ll find it easier to understand your build file.

Target

A target inherits all of the global options and may define its own. The name of the target is used to form the name of the output files for this target. The standard format is:

<project-name>-<target-name>.[css|jss]

The debug and uncompressed versions of the target’s output files are similar:

<project-name>-<target-name>-debug.[css|jss]
<project-name>-<target-name>-uncompressed.[css|jss]

The all target is assumed to be your primary target — and for simple projects, your only target — so the name of its output files do not include the target name.

Options

These options may be defined either for the entire project or only for a specific target.

name
This is the name of your project. This is the only required option and should be defined at the top of the project file.
version
What version is your project? You can insert this into your output files using the sequence @VERSION@.
mode
Anything other than release triggers debug/development mode.
tasks
What tasks should execute. Defaults to js, css, html. Basically everything.
targets
What targets should be built. I almost never use this one.
remove prefix
When source and asset file references get rewritten, it’s sometimes necessary to remove part of the path. For example, if all your Javascript and CSS reside within the src folder, but you don’t want a src folder in your output tree, you can remove it using this option.
output folder
Where should the build product go? The default is build.
external projects

What does your project depend upon? This is an array where the values may be either a folder containing another distil project or a structure defining how to build the external project.

external projects: ../coherent

is the same as:

external projects:
    - ../coherent

which is the same as:

external projects:
    - folder: ../coherent
    - build: distil
    - include: ../coherent/build

External projects may only be defined at the project level.

notice
It’s your code, this is your copyright statement. Or your manifesto. Or whatever. This notice will be prepended as a comment to every output file.
bootstrap file
When building the debug version of your Javascript library, this is the template for the Javascript file that will load each of your sources. Distil provides a default bootstrap file, but you can replace it if you really want to.
jsl conf
Where is your configuration file for Javascript Lint located. You don’t need to specify this unless you have a config file and you’ve put it in a clever location. By default, Distil will look for a jsl.conf file in the same folder as the build file then a .jsl.conf file in your home folder. If neither of those exist, the stock jsl.conf file included with Distil will be used.
jsdoc template
Have you created a custom template for JSDoc Toolkit? Great, put a reference to the template folder here and your docs will use your template rather than the default one included with Distil.
doc folder
Where do you want your docs to live? The default value is docs.
generate docs

Building Javascript documentation takes serious time. So the default value for this option is false. To build documentation, I use the following command line:

distil -target=webkit -generate-docs

This will build only the specified target (webkit) and turn on documentation for this build.

Conclusion

Although Distil is a work in progress, I’m using it to build a production system containing 130 Javascript files and 20 CSS files.

Making Javascript

Every development environment has its build tools: C and C++ have Make, Java has Ant, and Ruby has Rake. There are probably lots of other tools out there which I’ve never heard of, which is no slight to those tools, I just don’t get out much. But there aren’t many good tools for building Javascript projects1. And when you toss CSS and HTML into the mix, you’re pretty much on your own.

I expect a Javascript/CSS build tool to do all of the following:

My obsession with build tools started when I needed something for the Coherent javascript library. I wanted to automate running Javascript Lint against my source and package everything into a single file for efficient downloads. I admit the scope has increased considerably, but that was the original goal.

I’m taking advantage of several tools to make all this possible:

Of course, I’ve added a bit of code myself.

Lint Checking

When I was in college the world was young and dinosaurs roamed the Earth. One day, I’d just dashed into the lecture hall with a tyrannosaur hot on my heals, when the professor declared: “Lint is your best friend.” Of course, in those days, actually compiling your code took time — go get a coke from the machine down the hall kind of time — so using a tool that worked a bit quicker meant you could write better code.

Of course it’s ironic that we now have blazing fast computers, but a lint checker is still my best friend. In this case it’s the Javascript Lint checker by Matthias Miller. Because Javascript is interpreted, there’s no way to know whether you have a syntax error deep in the bowels of your code until you actually execute it. That’s where Matthias’ excellent tool comes in. He took the Mozilla Spidermonkey engine and added lots and lots of important checks for common coding mistakes.

This is an INDISPENSIBLE tool for Javascript development! It beats the pants off of Crockford’s JSLint, and catches most dumb errors before you get a runtime error.
— reviewer on the SourceForge home page for Javascript Lint

I’d definitely second this assessment. I didn’t like Crockford’s tool simply because it wasn’t really configurable (when I evaluated it) and I don’t need Crockford preaching at me through his tools.

File Concatenation

When building complicated Web applications, you’ll find yourself with quite a few lines of code that need to be deployed. To give this some scope, the Coherent library runs over 18,000 lines of code in 80 Javascript files. And my latest project runs close to 10,000 lines in 50 files plus the Dashcode library. This is great for development — where the files all live locally — but it starts to cause problems when you go to deploy.

Even with a Content Delivery Network (CDN) like Akamai or Amazon Cloudfront, you’re still looking at about 70ms latency for each file. If you have 50 Javascript files, that adds up to 3.5 seconds. If you have 50 files of your own and your library has 80, you’ll spend almost 10 seconds just downloading Javascript files. Of course, this holds true for CSS too — you pay a latency tax for each file you download.

The simple solution is to concatenate your Javascript and CSS files into fewer but larger files. This won’t reduce the number of bytes you have to ship down the wire, but we’ll solve that problem in a moment.

Dependency Management

You can’t just jam your CSS and Javascript files together and hope they work. In the case of CSS, your rules won’t cascade correctly and in Javascript, you’ll have undefined classes or variables. Therefore, it’s important to properly order your Javascript and CSS files.

For CSS files, the dependency mechanism relies on the CSS @import rule and the import rules are removed from the concatenated output. For Javascript files, I use the import declarations used by Javascript Lint (jsl) to control parsing of your Javascript files. For example, to include another Javascript file, use the comment:

/*jsl:import yourfile.js*/

This syntax isn’t ideal, but the real crime is that Javascript still lacks the basic ability to import other files. I’ve added code to jsl that will support finding imports outside of your current project tree — this works just like the include option to a C/C++ compiler or the CLASSPATH option to a Java compiler. This means imports that once looked like:

/*jsl:import ../../coherent/release/coherent-uncompressed.js*/

Are now a bit tidier:

/*jsl:import coherent-uncompressed.js*/

You won’t need to update a bunch of source files if you’re working in a branch and other projects aren’t in the same location.

Asset Management

There’s more to a project than just Javascript and CSS files. Your CSS files reference GIFs, JPGs, and PNGs; your Javascript files reference HTML templates. All these files need to be collected and copied to the build folder in order to package up the distribution. The build tool will issue a warning if it encounters a reference to an asset it can’t find. This is a huge help for those of us who occassionally forget to add files to the SVN repository.

For Javascript files, the assets are the HTML template files used to instantiate views. This might be somewhat specific to the library we’re using, but it’s probably not that unusual.

Each asset for an included file will be copied to the distribution folder. All references to the assets will be re-written to use paths relative to the distribution folder.

Documentation

As a Javascript developer, I’ve always envied the JavaDoc tool that Java developers get to use2. Of course, Java developers have to use Java, so they have troubles all their own. Javascript is a particularly difficult language to document, because it’s so dynamic. But Michael Mathews took on the challenge and produced JSDoc Toolkit.

Because Javascript doesn’t officially have classical inheritance with classes, Coherent-based projects use the following abstraction to define their classes:

project.MyView= Class.create(coherent.View, { ... });

Unfortunately, since this isn’t standard Javascript, JSDoc Toolkit has no way of understanding it. Fortunately, there’s a plug-in mechanism that’s more than up to the challenge. So with a little magic, documentation is a snap.

For the most part, JSDoc Toolkit uses a syntax just like JavaDoc. There are some extensions to handle the fact that there are no explicit types, but it’s all pretty simple.

Deployment

When deploying my application’s resources, I want the following:

  • Small files — by removing all the extra whitespace and comments, the Coherent library shrinks from 532K to 184K. My current project shrinks from 755K to 304K, which includes the variant of the Coherent library shipped with Apple’s Dashcode tool.
  • Compressed versions — although Web servers can compress my files on the fly, there’s no good reason to do that for static files. So by generating pre-gzipped versions of the output files, I increase response time by just a bit more.
  • A single folder — I want to be able to create a gzipped tar archive of my output folder and copy that to the debug, staging or production servers. Everything should be as easy as possible.

Ideally, each version of my static assets will have a unique URL: http://example.com/res/v1.2/myproject.js would be newer than http://example.com/res/v1.1/myproject.js. Using mod_expires, I can set the expiry date on the entire v1.2 folder to 1/1/2025 or something equally far in the future, because I know I’ll be changing the URL when I release a new version. This means that after the visitor has primed his browser cache, he’ll make no more network requests on future pages to fetch the same static resource.

To facilitate versioned static resources, I set my output-folder to ~/build/res/latest. That way the latest folder becomes a placeholder that can be generated by the server.

Development & Debugging

All of this is pointless if it makes development unpleasant. Really, who wants to rebuild between every little code change? If that’s what you want, go write Java or C# code.

The first helpful feature for active development is the debug version of the output files: target-debug.js or target-debug.css. If you include this file instead of the optimised version (target.js or target.css), you’ll get a file that will bootstrap all your original source files. For CSS, the debug version has one @import rule for each original source file. For Javascript, there’s some trickery to load each individual Javascript file. Performance will suffer, but it will make debugging possible.

In release mode, all your source files and assets are copied to the output folder. This means you can just tar-gzip the folder and push it out to the server. You can even add a little hook to your server that will switch over to the debug version of the output files

In dev mode, instead of copying all your source files, the build tool will create symlinks to the original source folders. This means when you’re loading the debug version of the output files, you can change the original files without worrying about having to rebuild. Of course, if you add a file or delete a file, you’ll need to re-run the build process, but that’s a heck of a lot better than running the build after each change.

Status

I’ll be wrapping up the code in the next few days. Provided I don’t get distracted playing Lego Star Wars with my daughter, you should be able to download something in a week or so…

Update 10/12/2009

The project is now called Distil3 and you can download the Ruby Gem from the Distil project on Google Code. I know Git and GitHub is more popular these days, especially with the Ruby crowd, but I just don’t get Git. Not yet anyway. Hey, give me some credit, I actually built a Gem…

Coming soon,


  1. A notable exception is Sprockets which is tailored for building the Prototype and Scriptaculous Javascript libraries. In addition to expecting you to write ruby scripts to configure your build, Sprockets doesn’t incorporate lint checking and it makes demands on how you use comments that I don’t particularly like. 

  2. On the other hand, the inline documentation tool for C# and .Net makes my eyes bleed. Since when has anyone considered XML a good tool for anything? Do you really need to do more XML push ups or pay the angle bracket tax

  3. Picking a project name is a real challenge. You want something that represents the project, but that isn’t either taken or stupid. There’s already a distil project on Google Code, but it’s empty at the moment. Of course, in spite of my google searches, I’m just as likely to discover a similarly named project that’s been around for ages. 

Declarative Syntax for Child Widgets

One of my goals for Coherent 1.1 is the option of using a declarative syntax to set up child widgets. This would greatly simplify the average init method and make the code a bit clearer and easier to understand. Read More

Favourable Reception for Coherent

I recently announced the upcoming release of Coherent 1.0 and I’ve been very pleased by the positive reception the library has received.

Lots of folks have come out of the woodwork to either say they’ve been looking for something like this for ages. I guess there are more fans of the Apple development model than I thought. And obviously a lot of us build Web applications either for a living or in our spare time.

As I’ve been working on some features I’ve planned for 1.1, I’ve run across some strange problems with selectors which seems to tie into John Resig’s recent blog post about selectors.