Writing CoffeeScript tests with angular-seed

Updated 5/4/2014 for Angular 1.2.16 and Karma 0.10.10.

The first thing I did, after snagging a copy of angular-seed, was try to write a test in CoffeeScript. It didn’t work out of the box, so I went hunting to figure out how to make it work.

(I also found a few GitHub projects that are prepackaged angular-seed with CoffeeScript, but I didn’t see any that have been kept up-to-date with new Angular versions like angular-seed has. Maybe that’ll change when Angular 1.2 ships. Nope, they’ve pretty much been abandoned.)

Why CoffeeScript rocks for Jasmine/Mocha tests

As awesome as nested describes are, they’re even more awesome in CoffeeScript.

Jasmine tests look like this:

describe('calculator', function() {
    it('adds', function() {
        expect(calculator.add(2, 2)).toBe(4);
    });
});

CoffeeScript has some nice advantages for code like this.

  1. CoffeeScript has a built-in lambda operator, ->, that replaces function() {. So that’s a little less typing, and a lot less of the line taken up by furniture.
  2. CoffeeScript doesn’t need curly braces. Instead, you indicate nested blocks by indenting them (and heck, you were doing that anyway).
  3. CoffeeScript lets you leave off the parentheses around a method’s arguments. (Unlike Ruby, you do still need the () if you’re not passing any arguments.)

Combine these, and you get code that looks like this:

describe 'calculator', ->
    it 'adds', ->
        expect(calculator.add 2, 2).toBe 4

Each line is shorter than before, so there’s less typing and a little better signal-to-noise. But you also don’t need those }); lines at the end, because you don’t need the close-curlies to end the lambdas, and you don’t need the close-parens to end the argument lists for describe and it — in both cases, the indentation suffices. Less ceremony, more essence.

Until you see it on your screen, it’s hard to appreciate just how much it improves your tests, not having all those }); lines. Many tests will consist of a single assertion, so by cutting that worthless extra line, you go from almost 33% noise-by-line-count to 0%. Plus, the entire test goes from three lines to two — so now you can fit 50% more tests on your screen. That’s a win.

The syntax is uncluttered enough that it can even be reasonable to put the whole test on one line:

describe 'calculator', ->
    it 'adds',      -> expect(calculator.add 2, 2).toBe 4
    it 'subtracts', -> expect(calculator.subtract 6, 2).toBe 4

When you’ve got a bunch of little tests that are all on the same theme, this can work really well. That would look a lot uglier if it was function() { instead of ->, and if you had to find a place for the }); as well.

And last but not least, it’s really easy to find a text editor that can do code folding based on indentation (and not as easy to find an editor that can collapse based on curly braces). I use Sublime Text, which is a bit pricey, but you can also do indentation-based code folding with the free SciTE if you put it in Python mode. So if you’ve got a whole nested describe for your calculator’s trig functions, and you want to collapse that because now you’re focusing on hex arithmetic, you just fold that whole section of your code up into a single line.

CoffeeScript tests with angular-seed

It takes some ritual to get angular-seed working with CoffeeScript, but the good news is, you only have to do it once (well, once per project).

Updated 5/4/2014: With Angular 1.2.16 and Karma 0.10.10, the “preprocessors” and “coffeePreprocessor” sections no longer need to be added, so I removed them from the instructions below. If you’re on a version where you do need them, you can copy them from the karma-coffee-preprocessor readme.

All the changes are in config/karma.conf.js. Look for the parts I tagged “STEP 1” and “STEP 2”.

module.exports = function(config){
    config.set({
    basePath : '../',

    files : [
      'app/lib/angular/angular.js',
      'app/lib/angular/angular-*.js',
      'test/lib/angular/angular-mocks.js',
      'app/js/**/*.js',
      // STEP 1: Add 'test/unit/**/*.coffee' to this list:
      'test/unit/**/*.coffee',
      'test/unit/**/*.js'
    ],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
            // STEP 2: Add 'karma-coffee-preprocessor' to this list:
            'karma-coffee-preprocessor',
            'karma-junit-reporter',
            'karma-chrome-launcher',
            'karma-firefox-launcher',
            'karma-jasmine'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

})}

Once that’s done, you’re off and running: you can drop a .coffee file in your project’s test/unit directory, and the test runner will pick up all the tests in it and run them.

Note that, at least with the current versions of angular-seed and jasmine, you’ll have to stop and restart scripts\test.bat before it’ll pick up the new test file. (This is true any time you add a new test file; it’s not CoffeeScript-specific.) Watch mode apparently doesn’t extend to watching for new test files. Just Ctrl+C to stop it, say Yes (or hit Ctrl+C again) to the “Terminate batch job (Y/N)?” prompt, and then run scripts\test.bat again, and you’re on your way. No longer an issue with Karma 0.10.10.

Editing CoffeeScript with Sublime Text 2

I’ve been playing with CoffeeScript and IcedCoffeeScript lately, and I’ve been looking for a decent editor for them. Not as easy as you might think.

I wanted an editor that Just Worked. And by “Just Worked”, I mean the basics, of course, like syntax highlighting, multiple editor tabs, and all that other stuff you take for granted. But I also mean that the editor should have these two features:

  • Code folding. If I’m writing Mocha tests (and man, CoffeeScript is awesome for writing Mocha tests!), I want to be able to collapse describes that I’m not actively working on at the moment.
  • Smart indentation. If I type -> and press Enter, I want the editor to indent the next line for me. (This isn’t just laziness — it’s also a hint to me when I forget to type ->, which I still do fairly often.)

It’s kind of depressing how much those two features narrow the field. Here’s all I wound up with:

  • SciTE. It doesn’t actually support smart indentation or CoffeeScript syntax highlighting, but I’ve been using it for years, it’s comfortable, and if you lie and tell it it’s editing Python, then you get all the code folding you want. Plus, it’s free. Far from great, but it’s at least adequate.
  • Cloud9 IDE. Its smart indentation is great, but when I looked at it last, it didn’t support code folding. (They claim that it does now, though they give no details on whether/how it works with CoffeeScript, and I haven’t bothered to sign up for an account to try it out.) Freemium, and also Web-based, which is intriguing; I may give this another look at some point.
  • JetBrains IDEA-based IDEs (IntelliJ IDEA, PyCharm, WebStorm, etc.) Commercial, $49 and up. I had already bought a license for PyCharm, and then they released 2.0 and added support for CoffeeScript, and I was eligible for a free upgrade. Code folding and smart indentation are there, all right, but so are a boatload of bugs — I could never use it without finding four or five bugs at a sitting. I wrote them all up, they fixed some of them in the next update but introduced more. Repeat. And again. I finally got frustrated and gave up.
  • Sublime Text 2 (the subject of the rest of this post). Commercial, $59.

I was a little bit amused to find that, even though I’m only interested in Windows, everything on this short list is cross-platform: Windows, Linux, and Mac OSX.

ST2

I’ve been using the free trial of Sublime Text 2 for the past couple of months (it’s fully functional, it just shows the occasional nag screen when you save). And I’ve been loving it, and finally sprung for a license today. (Note: not a paid advertisement. Just a happy customer.)

ST2 (as the Sublime Text 2 users like to call it) seems to be in perpetual beta. New versions come out every now and then, but the documentation is far from complete and there are the occasional rough edges. But that’s easily made up for by the fact that it rocks. It’s fast, it’s beautiful, it’s crazy powerful.

Package Control

How powerful? ST2 has a built-in Python console for scripting the editor. Scripts and customizations can be bundled into add-on packages (it’s even mostly-compatible with TextMate bundles, i.e. packages designed for the Mac TextMate editor). Packages can add everything from syntax-highlighting rules for new languages, to entirely new editor commands, to who knows what else (Python scripting, remember?)

Here’s the really awesome thing: somebody wrote a free add-on package, called Package Control, that is a GUI package manager for ST2, that runs inside ST2. You install it by pasting a single line (okay, a long line) of code into the Python console, which downloads and installs Package Control for you. And forever after, installing a new package is a matter of opening Package Control via ST2’s menu, selecting “Install”, typing a keyword search into the incremental-search box, and selecting the package you want — which it then downloads and installs in a second or two, and the new package is instantly live and working. Crazy fantastic.

Fast, powerful

The editor is always snappy and responsive — it scrolls like the scrollbar is glued to the mouse. There’s a sidebar that shows a miniature version of your whole file; it updates fast and scrolls fast, and you can recognize the shape of your code even though it’s approximately a 1.5-point font. There are a few animations in the editor, but they never slow you down; they just make the editor feel even more responsive.

There’s also an optional sidebar on the left, that shows a list of open files (which duplicates the tab bar, but I like it). If you create a “project” (basically just a list of directories you want to work with, which can be as simple as “.” or can include filtering and multiple locations), then the sidebar also shows a directory tree, which is a feature I’ve always liked and seldom found.

(These sidebars are optional, of course. You can turn them off, along with the tab bar and the status bar and even the menu bar. You can run ST2 fullscreen with no window chrome at all, if you like — they even have a shortcut to get you there called “distraction-free mode”.)

Autocomplete is interesting. ST2 is not an IDE; it doesn’t parse your code and understand which classes have which methods. Instead, it keeps track of all the different words in the current source file, and when you start typing a word, it will try to complete from that list. It’s surprising how well this simple, non-context-sensitive completion can work, especially since it’s lightning-quick.

Keystrokes

ST2 has a way of making mundane features awesome. For example, if you press Ctrl+D (select word), it also highlights all the other usages of that same word in the file. And then if you press Ctrl+D again, it will add the next one to your selection (that’s right, ST2 supports disjoint selection). Keep pressing Ctrl+D until you’ve highlighted all the usages inside a method, and then start typing a replacement — and hello, Sync Edit.

(I keep trying to use Ctrl+W to select word, because that’s what it is in ReSharper at my day job. But of course Ctrl+W is Close File, just like in Web browsers and SciTE and most everything else except Visual Studio. At least, if I close a tab by mistake, I can use Ctrl+Shift+T to reopen it — hooray for features stolen from Web browsers!)

Then again, there are the keystrokes that are just different for the sake of being different. You can do rectangular selections, but you can’t use the usual Alt+Shift+arrow keys; you have to use Ctrl+Alt+Up/Down followed by Shift+Left/Right. And for some reason, Ctrl+Shift+S defaults to Save As, instead of the more familiar (and far more useful) Save All.

You can customize all the keystrokes (and a lot of other things, for that matter) by editing JSON config files. There are convenient menu items to open these config files for editing right there inside ST2.

CoffeeScript and beyond

ST2 doesn’t support CoffeeScript out of the box, but there are free CoffeeScript and IcedCoffeeScript packages. (The IcedCoffeeScript package is a simple superset of the CoffeeScript one, so if you want both languages, it’s enough to just install the Iced package.) The easiest way to install them, of course, is via Package Control. Code folding works beautifully, though ST2 hides the folding arrows by default (to reduce screen clutter) — if you just move the mouse over the gutter, the folding arrows will appear. The smart indentation works great too. With the Iced package, ST2 is a perfect fit what I set out to find.

I’ve also installed the Task package (via Package Control, naturally), which gives you syntax highlighting for a minimalist “task” pseudo-language that lets you use ST2 as a to-do list (but without all the stupid limitations that most task-list apps want to give you, like “must be a single flat list” — here you can easily create hierarchies, multiple lists, whatever). Task highlights lines starting with a hyphen (“-“) as “undone tasks” and lines starting with a check mark (““) as “done tasks”, and it adds an editor command to toggle a line between – and ✓. This add-on isn’t very polished, but it’s still pretty nice to have. Unfortunately it’s tightly coupled to the color scheme you’ve selected in ST2; by default, with the Monokai scheme I prefer, everything shows up as plain white. You have to edit the package to make it work with your theme (instructions included in the readme). (For reference: with Monokai, it works well to set Task Title to markup.inserted, Completed Tasks to comment, Action Items to support.function, and the rest to keyword.)

If you use the Task package on Windows, you’ll run into the problem that the Consolas font (which ST2 uses by default on Windows) doesn’t support the “CHECK MARK” Unicode character. This is easily fixed: download and install the “DejaVu Sans Mono” font (yes, it distinguishes between O and 0, 1 and I and l; and it’s also useful for seeing Mocha’s output properly on Windows) and then add "font_face": "DejaVu Sans Mono" to your user settings file (Preferences > Settings – User; the file is in JSON format).

Where IDEA could win

The IDEA-based editors (PyCharm, WebStorm, etc.) have a couple of things going for them compared to ST2: IDEA is much more aggressive about highlighting syntax errors and warnings as you type, and it has a feature that auto-formats your code (prettyprinting), either on demand, or as you type. Both would be awesome features if they worked reliably.

Unfortunately, both of those features have been major bug farms in the IDEA IDEs. They highlight “errors” in code that’s perfectly valid, they “reformat” code in ways that cause it to become non-compilable or that subtly change its meaning, etc. And of course, if CoffeeScript adds a new feature (I’ll bet do (x = 1, y = 2) ->, just added in CoffeeScript 1.3.1, is going to give them problems), then you have to submit an enhancement request and wait for them to add the feature and ship a new point release — which means you’re stuck with false-positive error highlighting for a good long while. ST2 packages are typically open-source, so can be a lot more agile.

I’m sure IDEA will be pretty awesome for CoffeeScript if JetBrains ever use it to dogfood some real CoffeeScript development of their own, and actually shake out some of the bugs before they ship (and add some actual regression tests, and everything else it would take to make it stable). But until that happens, if you’re easily frustrated, then don’t use IDEA for CoffeeScript.

So how about that extensibility?

I haven’t actually done much yet to extend ST2 on my own. The big hurdle here is that, by and large, you extend ST2 by writing TextMate packages, and I haven’t been able to find any documentation on how to write a TextMate package, beyond “open TextMate, tell it to create a new package, and use the GUI to specify all the options”. Since I don’t have a Mac, and since TextMate is Mac-only, that doesn’t help me much.

But hey. Even just using extensions other people have written, ST2 is just plain fun to use. I think it’s well worth the modest price.

CoffeeScript’s for loops don’t work on strings (despite appearances to the contrary)

I’ve been playing around with CoffeeScript, and found myself needing to loop over the characters (and indexes) in a string. So I tried a simple CoffeeScript for loop:

for char, index in line
    # ...

I ran it, and it worked, even in IE, so I assumed that CoffeeScript was doing whatever magical syntax was required to make it work everywhere.

Later I found out I was wrong. CoffeeScript was generating the syntax that works with arrays — list[index] — and that syntax only happens to sometimes work with strings. It works consistently in Chrome, and FireFox, and Opera. It even works in IE… but only if you have an HTML5 doctype.

<!DOCTYPE html>
<script>
document.write('abc'[1]);
</script>

The above HTML page works fine in IE9, outputting b as expected. But if you remove the <!DOCTYPE html> line, IE outputs undefined instead. That’s right, it loads a completely different version of JavaScript depending on what version of HTML you’re using! And if you load the above page into a WPF Frame, even the doctype won’t save you — it always outputs undefined.

When I went back and looked through the CoffeeScript documentation, sure enough, nowhere do they actually say that for loops can be used to iterate through the characters in a string. The fact that it ever works is apparently an accident, so don’t rely on it.

If you need to loop over the characters in a string, the right thing is to loop over the indexes and use charAt: See update below

for index in [0...line.length]
    char = line.charAt index
    # ...

Edit: Here’s a better way, suggested by the kind CoffeeScript folks:

for char, index in str.split ''

.NET and CoffeeScript: comparing Jurassic, Jint, and IronJS

Recently I went looking for ways to write a .NET desktop app that could compile CoffeeScript to JavaScript. There are already several NuGet packages for exactly this, but most of them look like they’re tightly bound to ASP.NET. So I struck out on my own, following the general steps in “CoffeeDemo – A Simple Demo of IronJS, using CoffeeScript”. The main CoffeeScript compiler is written in CoffeeScript, but they also provide one written in JavaScript, so my basic outline was:

  1. Instantiate a JavaScript engine.
  2. Tell the JavaScript engine to run coffee-script.js. This creates the CoffeeScript object and its compile method.
  3. Tell the JavaScript engine to call CoffeeScript.compile, and pass a string containing the CoffeeScript code to compile.

But my first attempt ran slower than I’d hoped for. (Well, coffee-script.js is 163 KB, and that’s the minified version! So I guess it does have a lot to do.)

I decided to find out whether I could do better: I tried several different JavaScript-in-.NET implementations, to see which one would perform the best. I tested Jurassic, Jint, and IronJS. My results are below, along with the C# code in case anyone is interested in seeing the minor differences between the APIs.

In all three cases, the coffeeCompiler parameter contains the 1.1.2 version of coffee-script.js, as downloaded from GitHub; and the input parameter contains a one-line CoffeeScript script:

alert "Hello world!"

Jurassic

Jurassic dynamically compiles JavaScript to CLR code at runtime, so you take a performance hit the first time you run some JS, but it should be pretty fast after that. Jurassic is available via NuGet.

private void CompileCoffeeScriptUsingJurassic(
    string coffeeCompiler, string input)
{
    Console.WriteLine("Jurassic");
    var stopwatch = Stopwatch.StartNew();
    Console.WriteLine(stopwatch.Elapsed + ": Creating engine");
    var engine = new ScriptEngine();
    Console.WriteLine(stopwatch.Elapsed + ": Parsing coffee-script.js");
    engine.Execute(coffeeCompiler);
    Console.WriteLine(stopwatch.Elapsed + ": Adding compile wrapper");
    engine.Execute("var compile = function (src) " +
        "{ return CoffeeScript.compile(src, { bare: true }); };");
    Console.WriteLine(stopwatch.Elapsed + ": Compiling CoffeeScript input");
    var output = engine.CallGlobalFunction("compile", input);
    Console.WriteLine(stopwatch.Elapsed + ": Done");
    Console.WriteLine("Output:");
    Console.WriteLine(output);
    Console.WriteLine();
}

Jint

Jint is a JavaScript interpreter. It’s not available through NuGet yet, but it’s a single DLL.

private void CompileCoffeeScriptUsingJint(
    string coffeeCompiler, string input)
{
    Console.WriteLine("Jint");
    var stopwatch = Stopwatch.StartNew();
    Console.WriteLine(stopwatch.Elapsed + ": Creating engine");
    var engine = new JintEngine();
    Console.WriteLine(stopwatch.Elapsed + ": Parsing coffee-script.js");
    engine.Run(coffeeCompiler);
    Console.WriteLine(stopwatch.Elapsed + ": Adding compile wrapper");
    engine.Run("var compile = function (src) " +
        "{ return CoffeeScript.compile(src, { bare: true }); };");
    Console.WriteLine(stopwatch.Elapsed + ": Compiling CoffeeScript input");
    object output = null;
    try
    {
        output = engine.CallFunction("compile", input);
    }
    catch (JsException ex)
    {
        Console.WriteLine("ERROR: " + ex.Value);
    }
    Console.WriteLine(stopwatch.Elapsed + ": Done");
    Console.WriteLine("Output:");
    Console.WriteLine(output);
    Console.WriteLine();
}

IronJS

IronJS is based on the DLR, so it seemed like it might strike a great balance between upfront compile time and runtime — after all, that’s what the DLR is all about.

IronJS is available through NuGet — there’s both an IronJS.Core (standalone) and an IronJS (depends on IronJS.Core), with nothing to explain the difference between the two; but at least for this code, you only need IronJS.Core.

private void CompileCoffeeScriptUsingIronJs(
    string coffeeCompiler, string input)
{
    Console.WriteLine("IronJS");
    var stopwatch = Stopwatch.StartNew();
    Console.WriteLine(stopwatch.Elapsed + ": Creating engine");
    var engine = new CSharp.Context();
    Console.WriteLine(stopwatch.Elapsed + ": Parsing coffee-script.js");
    engine.Execute(coffeeCompiler);
    Console.WriteLine(stopwatch.Elapsed + ": Adding compile wrapper");
    engine.Execute("var compile = function (src) " +
        "{ return CoffeeScript.compile(src, { bare: true }); };");
    Console.WriteLine(stopwatch.Elapsed + ": Fetching compile wrapper");
    var compile = engine.GetGlobalAs<FunctionObject>("compile");
    Console.WriteLine(stopwatch.Elapsed + ": Compiling CoffeeScript input");
    var result = compile.Call(engine.Globals, input);
    var output = IronJS.TypeConverter.ToString(result);
    Console.WriteLine(stopwatch.Elapsed + ": Done");
    Console.WriteLine("Output:");
    Console.WriteLine(output);
    Console.WriteLine();
}

The results

Jurassic
00:00:00.0000063: Creating engine
00:00:00.1540545: Parsing coffee-script.js
00:00:03.4346969: Adding compile wrapper
00:00:03.4408860: Compiling CoffeeScript input
00:00:05.3466983: Done
Output:
alert("Hello world!");

Jint
00:00:00.0000019: Creating engine
00:00:00.2617049: Parsing coffee-script.js
00:00:02.5270733: Adding compile wrapper
00:00:02.5295317: Compiling CoffeeScript input
ERROR: Parse error on line 2: Unexpected 'STRING'
00:00:02.5832895: Done
Output:


IronJS
00:00:00.0000019: Creating engine
00:00:00.2282421: Parsing coffee-script.js
00:00:55.5590620: Adding compile wrapper
00:00:55.5629230: Fetching compile wrapper
00:00:55.5642908: Compiling CoffeeScript input
00:01:17.8580574: Done
Output:
alert("Hello world!");

Jint wasn’t up to the task — it got a weird error when trying to call CoffeeScript.compile. I played with this a bit, and found that it would work if I passed an empty string, but give errors with non-blank CoffeeScript to compile; sometimes a string error like above, sometimes a weird error about multiline comments. It’s too bad, because Jint shows a lot of promise, speed-wise. I don’t know what the problem is; the error didn’t give me much to go on, and I’m not terribly motivated to pursue the problem when the other libraries work. (I did write this up in their bugtracker, though — it’s issue #6928.)

I was surprised that IronJS was so much slower than the others — about 20x slower than Jint at running coffee-script.js, and about 10x slower than Jurassic. This is especially puzzling since the article I based my code on mentions a “compilation lag”. To me, 55 seconds is hardly “lag”!

The winner here (and coincidentally the first one I tried) is Jurassic — so the performance that disappointed me is also the best I’m likely to get. On my laptop, you take about a 3.5-second penalty to compile coffee-script.js, and then another two seconds to run CoffeeScript.compile on a one-line script.

I did find that subsequent calls to CoffeeScript.compile were nearly instantaneous with all three libraries. So Jurassic’s 2 seconds is probably due to the JIT compiler running for the first time on that runtime-generated code. Not sure what to make of the 20 seconds for IronJS; is the DLR just that big?