You can probably file this post under “bad ideas.” But what the heck, if you can't do a few crazy things when blogging every single day, then what's the point of blogging every day?
Here's the thing: I love testing in Dart and I hate testing in JavaScript. So the obvious question becomes… can I test my JavaScript with Dart?
I start by making the Funky Backbone Calendar from Recipes with Backbone into a Dart project. This is accomplished simply by adding a
pubspec.yaml
file naming my project and listing the dependencies:name: funky_calendar dependencies: unittest: any plummbur_kruk: any intl: any js: anySince I am testing, I need unittest. Dart does not facilitate any way to stub requests, so I will use a “real fake” server (plummbur_kruk) to test my Backbone code. The
intl
is needed because Dart no longer supports converting dates to ISO 8601 strings (sigh). Finally, I will need the js-interop package to interact with the JavaScript code being tested.I install my dependencies:
➜ calendar git:(dart-testing) ✗ pub install Resolving dependencies.................. Dependencies installed!Now I add the test context web page:
<html> <head> <title>Funky Calendar Test Suite</title> <!-- Library files --> <script type="text/javascript" src="../public/javascripts/jquery.min.js"></script> <script type="text/javascript" src="../public/javascripts/jquery-ui.min.js"></script> <script type="text/javascript" src="../public/javascripts/underscore.js"></script> <script type="text/javascript" src="../public/javascripts/backbone.js"></script> <!-- include source files here... --> <script type="text/javascript" src="../public/javascripts/calendar.js"></script> <!-- Dart tests... --> <script type="application/dart" src="test.dart"></script> <script type='text/javascript'> var testRunner = window.testRunner || window.layoutTestController; if (testRunner) { function handleMessage(m) { if (m.data == 'done') { testRunner.notifyDone(); } } testRunner.waitUntilDone(); window.addEventListener("message", handleMessage, false); } </script> <script src="packages/browser/dart.js"></script> </head> <body> </body> </html>It is probably time to move that
testRunner
code at the bottom into a package. It prevents tests from completing until the suite signals that all have been run. Actually, I hope that unittest will make that work soon-ish. The rest of the HTML loads the Backbone and associated JavaScript code, along with the test.dart
file.As for
test.dart
, I copy the skeleton from various other projects. It has a main()
entry point that will run my tests. It also polls for all of the tests being done, communicating back to the testRunner
in the web page context:library funky_calendar_test; import 'package:unittest/unittest.dart'; import 'package:plummbur_kruk/kruk.dart'; import 'dart:html'; import 'dart:async'; import 'package:intl/intl.dart'; final iso8601 = new DateFormat('yyyy-MM-dd'); main() { // tests will go here... pollForDone(testCases); } pollForDone(List tests) { if (tests.every((t)=> t.isComplete)) { window.postMessage('done', window.location.href); return; } var wait = new Duration(milliseconds: 100); new Timer(wait, ()=> pollForDone(tests)); }For my first test, I will populate the backend REST-like interface in plummbur-kruk and expect that the record shows up in the Backbone app:
var el;
setUp((){
document.head.append(new BaseElement()..href = Kruk.SERVER_ROOT);;
el = document.body.append(new Element.html('<div id=calendar>'));
var today = new DateTime.now(),
fifteenth = new DateTime(today.year, today.month, 15),
doc = '''
{
"title": "Get Funky",
"description": "asdf",
"startDate": "${iso8601.format(fifteenth)}"
}''';
return Future.wait([
Kruk.create(doc),
Kruk.alias('/widgets', as: '/appointments')
]);
});
tearDown((){
el.remove();
return Kruk.deleteAll();
});
test("populates the calendar with appointments", (){
});
I run the test suite with a familiar Bash script:#!/bin/bash # Start the test server packages/plummbur_kruk/start.sh # Run a set of Dart Unit tests results=$(content_shell --dump-render-tree test/index.html 2>&1) echo -e "$results" # Stop the server packages/plummbur_kruk/stop.sh # check to see if DumpRenderTree tests # fails, since it always returns 0 if [[ "$results" == *"Some tests failed"* ]] then exit 1 fi echo echo "Looks good!"And the test actually passes:
➜ calendar git:(dart-testing) ✗ ./test/run.sh Server started on port: 31337 [2013-09-19 23:52:12.093] "POST /widgets" 201 [2013-09-19 23:52:12.132] "POST /alias" 204 [2013-09-19 23:52:12.140] "DELETE /widgets/ALL" 204 CONSOLE MESSAGE: unittest-suite-wait-for-done CONSOLE MESSAGE: PASS: the initial view populates the calendar with appointments CONSOLE MESSAGE: CONSOLE MESSAGE: All 1 tests passed. CONSOLE MESSAGE: unittest-suite-success ... Looks good!Of course, my test is currently empty so I can't quite claim victory yet. And here, I see why this might not be a great idea. Since I have to wait for the JavaScript files to load and be evaluated, I add a delay before my test:
test("populates the calendar with appointments", (){
new Timer(
new Duration(milliseconds: 500),
expectAsync0((){
var foo = new js.Proxy(js.context.Cal, query('#calendar'));
js.context.Backbone.history.loadUrl();
})
);
});
In addition to making that uglier, it still fails to work. I am getting weird js-interop isolate errors:FAIL: the initial view populates the calendar with appointments Caught The null object does not have a method 'callSync'. NoSuchMethodError : method not found: 'callSync' Receiver: null Arguments: [GrowableObjectArray len:0] dart:core-patch/object_patch.dart 20:25 Object.noSuchMethod package:js/js.dart 756:35 _enterScope package:js/js.dart 735:28 _enterScopeIfNeeded package:js/js.dart 724:22 context ../../test.dart 50:37
packages/browser/interop.js
script for js-interop from the testing context page. Things still don't quite work even with that, but I'm getting closer.But this seems promising enough to continue tomorrow. Well, at least fun enough.
Day #879
No comments:
Post a Comment