Test.Simple 0.10 Released

I’m pleased to announce the first beta release of Test.Simple, the port of Test::Builder, Test::Simple, Test::More, and Test::Harness to JavaScript. You can download it here. See the harness in action here (or verbosely!). This release has the following changes:

  • Changed the signature of functions passed to output() and friends to accept a single argument rather than a list of arguments. This allows custom functions to be much simpler.
  • Added support for Macromedia Director. Patch from Gordon McCreight.
  • Backwards Incompatibility change: moved all modules into Test namespace by using an object for the Test namespace and assigning the Build() constructor to it. See http://xrl.us/fy4h for a description of this approach.
  • Fixed the typeOf() class method in Test.Builder to just return the value returned by the typeof operator if the class constructor is an anonymous function.
  • Changed for (var in in someArray) to for (var i = 0; i < someArray.length; i++) for iterating through arrays, since the former method will break if someone has changed the prototype for arrays. Thanks to Bob Ippolito for the spot!
  • The default output in browsers is now to append to an element with the ID test or, failing that, to use document.write. The use of the test element allows output to continue to be written to the browser window even after the document has been closed. Reported by Adam Kennedy.
  • Changed the default endOutput() method to be the same as the other outputs.
  • Backwards incompatibility change: Changed semantics of plan() so that it takes an object for an argument. This allows multiple commands to be passed, where the object attribute keys are the command and their values are the arguments.
  • Backwards incompatibility change: Changed the no_plan, skip_all, and no_diag (in Test.More only) options to plan() to their studlyCap alternatives, noPlan, skipAll, and noDiag. This makes them consistent with JavaScript attribute naming convention.
  • Added beginAsync() and endAsync() methods to Test.Builder to allow users to put off the ending of a script until after asynchronous tests have been run. Suggested by Adam Kennedy.
  • Backwards incompatibility change: Changed the signature for the output() method and friends to take only a single anonymous function as its argument. If you still need to call a method, pass an anonymous function that calls it appropriately.
  • Changed handling of line-endings to be browser-specific. That is, if the current environment is Internet Explorer, we use \r for line endings. Otherwise we use \n. Although IE properly interprets \n as a line ending when it's passed to document.write(), it doesn't when passed to a DOM text node. No idea why not.
  • Added a browser harness. Now you can run all of your tests in a single browser window and get a summary at the end, including a list of failed tests and the time spent running the tests.
  • Fixed calls to warn() in Test.More.
  • Output to the browser now causes the window to scroll when the length of the output is greater than the height of the window.
  • Backwards incompatibility change: Changed all instances of Ok to OK. So this means that the new Test.More function names are canOK(), isaOK(), and cmpOK(). Sorry 'bout that, won't happen again.
  • Ported to Safari (though there are issues--see the Bugs section of the Test.Harness.Browser docs for details).

Obviously this is a big release. I bumped up the version number because there are a fair number of backwards incompatibilities. But I'm reasonably confident that they wont' change so much in the future. And with the addition of the harness, it's getting ready for prime time!

Next up, I'll finish porting the test from Test::Harness (really!) and add support for JSAN (look for a JSAN announcement soon). But in the meantime, feedback, bug reports, kudos, complaints, etc.warmly welcomed!

Backtalk

Bob Ippolito wrote:

Excellent!

I didn't even notice the Safari bugs in the previous release.. I got along just fine.. but perhaps I was just writing my test suite defensively enough that it didn't break anything.

Aside from changing the includes, this (diff) was all I had to do in order to upgrade, where bind(func, obj) does the obvious:

-    plan('tests', 46);
+    var test = new Test.Builder();
+    test.plan({'tests': 46});
+    var is = bind(test.isEq, test);
+    var ok = bind(test.ok, test);

BTW: I didn't see any documentation that "is" was removed/renamed, but perhaps I didn't look hard enough.

Theory wrote:

Re: Excellent!

Bob,

is() was not removed. It's in Test.More, not Test.Builder. Try this:

  <script src="js/Test/Builder.js"></script>
  <script src="js/Test/More.js"></script>
  <script>
    plan({ tests: 46});
    is(1, '1', 'Got one?');
  </script>

HTH!

—Theory

Bob Ippolito wrote:

That's bizarre, because it didn't work until I made those bindings. I am including Test/More.js , maybe it was some kind of stale cache sludge screwing things up.

broquaint wrote:

Why increment when you can iterate

Aristotle demonstrates a nice way to iterate through arrays without having to use the ubiquitous incrementation of the array index at Javascript instant iterators.

Bob Ippolito wrote:

Wow, I bet that that style of iterator makes for some really inefficient JavaScript code. Unfortunately, that matters, because the implementations of JavaScript in today's browsers are pathetically slow already. Adding a constant significant overhead to every iteration of every loop is not something I'd do.. It's generally better not to "fight the framework" so much because it fights back. For example, if you start hacking the Object prototype (*cough*Prototype*cough*), then property iteration no longer works correctly; which is why David reverted to for(;;) loops.

Theory wrote:

Re: Why increment when you can iterate

broquaint,

Cool technique. I'll keep that in mind for when I create Iterator.js. ;-)

Bob,

Performance is something the browsers will have to fix at some point. So far, I have no complaints (these tests run really fast!). In general, I'll take style over performance, and will probably put Aristotle's iterator technique to use one day (it doesn't look that slow to me, and does not use for (var in)). But I will leave things as they are in my Test modules so as to keep things as simple and portable as possible.

Cheers!

—Theory

Bob Ippolito wrote:

Re: Why increment when you can iterate

Performance is something browsers will have to fix, but are very, very, very far from fixing. Another assumption that the iterator makes is that a value that evaluates to false is the end of the iteration Function invocation is slow in JS, especially when you have a closure, and this adds at least two function invocations per iteration.