Monday, September 30, 2013

A First Pass at Testing a Polymer.dart Element


When I try to solve problems, I keep things as simple as possible. When I am creating problems, I don't mind a little complexity. With that in mind, I start trying to test a Polymer.dart tonight.

I have a nice little <ice-code-editor> custom element, thanks to Polymer. With two simple options, I can embed a slick editor that superimposes Three.js code on top of a nifty preview:



The underlying Dart code in the ICE Code Editor is pretty complex. Among other things, there is an HTTP request for the code to be used and js-interop to work with the actual code editor.

Regardless of the underlying complexity involved, I have two obvious ways to test. I can test the published interface—the attributes of the custom element and the corresponding instance variables in the backing class. Additionally, I can test the resultant HTML changes once my Polymer element has rendered. I think the former is likely the easier of the two—especially with this code—but there is only one way to be sure.

To make things easier to access from the test directory, I move my template and backing code into lib/polymer:
➜  ice-code-editor git:(polymer) ✗ tree lib/polymer 
lib/polymer
├── ice_code_editor_element.dart
└── ice_code_editor.html

0 directories, 2 files
Thanks to the magic of Dart Pub, this will allow me to access those as packages/ice_code_editor/polymer/ice_code_editor.html regardless of where I am in the package.

In test/polymer, I create a test page that will try to use the custom <ice-code-editor> element:
<html>
  <head>
    <link rel="import" href="packages/ice_code_editor/polymer/ice_code_editor.html">
    <script src="packages/polymer/boot.js"></script>
    <script src="packages/unittest/test_controller.js"></script>

    <script src="packages/browser/interop.js"></script>

    <script type="application/dart" src="test.dart"></script>
  </head>

  <body>
    <ice-code-editor src="embed.html" line_number="0"></ice-code-editor>
  </body>
</html>
Hopefully that will load the necessary test and application code, then create an embedded version of the custom element to be tested.

When I load it up with a dummy test in test.dart, however, I get the following error in the console:
XMLHttpRequest cannot load 
file:///home/chris/repos/ice-code-editor/test/polymer/packages/ice_code_editor/polymer/ice_code_editor.html. 
Cross origin requests are only supported for HTTP. 
Bother.

I am unsure how I am to get around this since most Dart testing—at least dart:html testing—uses local file:// based tests. This particular error is coming from the guts of the Polymer.dart code, so I tend to doubt that I can find an easy workaround.

Without any other obvious options tonight, I move the test page context and test code into web/test, then access the page over pub serve, the dummy server built into Pub. With that, I get a passing test:



That is not quite a long term solution for testing, however. I do not believe that pub serve was intended to be used in unit testing. If I want to test like this, I may have to write or use a simple test server to do something like this. Unless I am missing a more obvious solution.

Regardless, I have more questions than answers on my hands right now, which is where I like to be when learning new stuff. So in that regards, my start-with-the-complex strategy has been wildly successful. Yay?

Day #890

Sunday, September 29, 2013

Kicking the Tires on the Proposed new Polymer.dart


After a few days trying, I apply the proposed patch to Polymer.dart in a rather tedious way. So that I can use it in my ICE Code Editor project, I point the project's pubspec.yaml to my local copy of Dart + Packages using a path dependency:
name: ice_code_editor
# ...
dependencies:
  # ...
  polymer: {path: /home/chris/repos/dart/dart/pkg/polymer}
Then, I make similar updates to the core packages that are impacted by the patch. For each, I replace the “any” constraint with a relative path constraint. The diff ends up being longish:
diff --git a/dart/pkg/custom_element/pubspec.yaml b/dart/pkg/custom_element/pubspec.yaml
index e52b080..3e1cf52 100644
--- a/dart/pkg/custom_element/pubspec.yaml
+++ b/dart/pkg/custom_element/pubspec.yaml
@@ -9,6 +9,6 @@ dependencies:
   meta: any
   mutation_observer: any
   # TODO(jmesserly): fix this, we should not depend on MDV.
-  mdv: any
+  mdv: {path: ../mdv}
 dev_dependencies:
   unittest: any
diff --git a/dart/pkg/mdv/pubspec.yaml b/dart/pkg/mdv/pubspec.yaml
index 937a8cc..85c7ff5 100644
--- a/dart/pkg/mdv/pubspec.yaml
+++ b/dart/pkg/mdv/pubspec.yaml
@@ -9,6 +9,6 @@ description: >
 homepage: https://www.dartlang.org/polymer-dart/
 dependencies:
   logging: any
-  observe: any
+  observe: {path: ../observe}
 dev_dependencies:
   unittest: any
diff --git a/dart/pkg/polymer/pubspec.yaml b/dart/pkg/polymer/pubspec.yaml
index acd745f..38206af 100644
--- a/dart/pkg/polymer/pubspec.yaml
+++ b/dart/pkg/polymer/pubspec.yaml
@@ -11,15 +11,15 @@ dependencies:
   barback: any
   browser: any
   csslib: any
-  custom_element: any
+  custom_element: {path: ../custom_element}
   html5lib: any
   html_import: any
   logging: any
-  mdv: any
+  mdv: {path: ../mdv}
   meta: any
-  observe: any
+  observe: {path: ../observe}
   path: any
-  polymer_expressions: any
+  polymer_expressions: {path: ../polymer_expressions}
   shadow_dom: any
   source_maps: any
   yaml: any
diff --git a/dart/pkg/polymer_expressions/pubspec.yaml b/dart/pkg/polymer_expressions/pubspec.yaml
index 3a1d563..f6641b7 100644
--- a/dart/pkg/polymer_expressions/pubspec.yaml
+++ b/dart/pkg/polymer_expressions/pubspec.yaml
@@ -4,7 +4,7 @@ description: An expressive custom binding syntax for MDV templates
 homepage: http://www.dartlang.org/polymer-dart/
 dependencies:
   browser: any
-  mdv: any
-  observe: any
+  mdv: {path: ../mdv}
+  observe: {path: ../observe}
 dev_dependencies:
   unittest: any
The patch affected the polymer, observe, and custom_element core packages. These, along with the other core packages in Dart (the ones maintained by the Dart developers), all reside in the same directory in the main Dart source code repository. So I had to manually update my local copy of polymer so that it depended on observe and custom_element, using relative paths. Unfortunately, both the mdv and polymer_element packages also depended on observe, so they got dragged into the pubspec.yaml fun as well.

This was not horrible, but hopefully there will be easier ways to pin packages in the future. Anyhow, with the preliminaries out of the way, pub install works:
➜  ice-code-editor git:(polymer) ✗ pub install
Resolving dependencies....................
Dependencies installed!
And the new versions of the code are served up via pub serve when I try my code out in Dartium. So how does the patch work with my Polymer.dart code?

I am still trying to read two attributes from my custom <ice-code-editor> element, which is declared as:
<polymer-element name="ice-code-editor" attributes="src,lineNumber">
  <template>
    <h1>The src is "{{src}}" ({{lineNumber}})</h1>
    <content></content>
  </template>
  <script type="application/dart" src="ice_polymer.dart"></script>
</polymer-element>
Just for debugging purposes, I am also including the values of src and lineNumber inside an <h1> tag in this template.

The backing ice_polymer.dart code declares the same src and lineNumber attributes as instance variables in the IceCodeEditorElement class:
import 'package:polymer/polymer.dart';
// ICE-related imports here...
@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src = '';
  @observable int lineNumber = 0;

  void created() {
    super.created();
    // Setup ICE here...

    HttpRequest.getString(src).then((response) {
      editor.content = response;
      editor.editorReady.then((_)=> editor.lineNumber = lineNumber);
    });

  }
}
The src attribute is used to request the content for the editor (it will be located in a separate file). The lineNumber attribute is used to scroll the <ice-code-editor> widget to the correct location in the buffer.

Finally, I use the custom element as:
<head>
  <link rel="import" href="ice_code_editor.html">
  <script src="packages/polymer/boot.js"></script>
</head>
<body>
<!-- other HTML here... -->
<ice-code-editor src="embed_a.html" line-number="49"></ice-code-editor>
<!-- ... -->
And, once all of the pieces—the patch and my local code—are together, it works. Kind of…

The src attribute is working, because when I view the page, the contents are correctly placed in the editor and the <h1> element is correctly updated:



But the line-number attribute / lineNumber instance variable are not working.

Unlike pre-patch, this does not appear to be a problem with types. I can get this working if I use the same name for both the attribute and the instance variable. If, for example, I replace every instance of line-number and lineNumber with line_number in the custom element, its template, and the backing code, it works:



Interestingly, this even works if the instance variables in the backing class are declared without defaults (something that did not work pre-patch):
@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src;
  @observable int line_number;
  // ...
}
I dig through the patched code a bit and it seems that there really is no support for camel-cased attribute mapping as there used to be. Some of the code has survived (it's just no longer used), so hopefully that will make its way back before or after the patch is ready.

While digging around the patched packages, I find that the @observable annotations look to be getting replaced with @published, which has a nicer, semantic feel:
@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @published String src;
  @published int line_number;
  // ...
}
So in the end, the patch was a pain to apply, but it seems well worth it. The confusion with type information seems nicely resolved by the patch. I am not too fussed with the lack of hyphenated / camel-case conversion. It seems an easy enough thing to add back at some point in the future and, until then, attributes like line_number are fine by me.


Day #889

Saturday, September 28, 2013

Strike Two: Using Big Dart Package Patches


My first attempt at working with a large Dart patch was not quite as smooth as I would have liked. The problem is that the patch spans several Dart packages. Applying the patch was easy enough, but getting my local code to work with the local packages resulted in something akin to dependency hell:



Those are just a few of the changes that I needed to make—not only to the packages that are directly impacted by the patch—but also to the packages that rely on the same code. The problem is that Dart's Pub package system will not allow the same packages to be installed from two different sources (e.g. pub.dartlang.org and the filesystem).

It might be nice for Pub to defer an “any” constraint in favor of a “path” one. That is, if the various packages in the dependency tree includes a bunch of mdv: any dependency and a single mdv: path: /home/chris/repos/dart/dart/pkg/mdv dependency, then the latter could be used everywhere. No matter, this is an edge case for an admittedly early-stage tool like Pub. In the meantime, how might I better handle development in this case?

I try switching my project's dependencies back to the default pub.dartlang.org by reverting the constraints to “any” in pubspec.yaml:
name: ice_code_editor
# ...
dependencies:
  # ...
  polymer: any
After a pub install, a quick check of the project's packages directory shows that the three packages that were addressed by the original patch—polymer, custom_element, and observe—are now coming from pub.dartlang.org:
➜  ice-code-editor git:(polymer) ✗ ls -l packages/{polymer,custom_element,observe}
lrwxrwxrwx 1 chris chris 71 Sep 28 20:07 packages/custom_element -> 
    /home/chris/.pub-cache/hosted/pub.dartlang.org/custom_element-0.7.5/lib
lrwxrwxrwx 1 chris chris 64 Sep 28 20:07 packages/observe -> 
    /home/chris/.pub-cache/hosted/pub.dartlang.org/observe-0.7.5/lib
lrwxrwxrwx 1 chris chris 64 Sep 28 20:07 packages/polymer -> 
    /home/chris/.pub-cache/hosted/pub.dartlang.org/polymer-0.7.5/lib
I remove the symbolic links to the cached, hosted versions of those packages, replacing them with my modified local versions instead:
➜  ice-code-editor git:(polymer) ✗ cd packages 
➜  packages git:(polymer) ✗ rm {polymer,observe,custom_element}
➜  packages git:(polymer) ✗ ln -s ~/repos/dart/dart/pkg/polymer/lib polymer
➜  packages git:(polymer) ✗ ln -s ~/repos/dart/dart/pkg/observe/lib observe    
➜  packages git:(polymer) ✗ ln -s ~/repos/dart/dart/pkg/custom_element/lib custom_element
That seems to work. At the very least, the built-in pub serve web server starts up without complaint:
➜  ice-code-editor git:(polymer) ✗ pub serve
Serving ice_code_editor on http://localhost:8080
Build completed successfully
Sadly, appearances are deceiving in this case. The updated polymer_element.dart file is nearly empty:
➜  ice-code-editor git:(polymer) ✗ cat packages/polymer/polymer_element.dart
// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library polymer.polymer_element;

export 'polymer.dart' show PolymerElement, registerPolymerElement;

➜  ice-code-editor git:(polymer) ✗ cat web/packages/polymer/polymer_element.dart
// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library polymer.polymer_element;

export 'polymer.dart' show PolymerElement, registerPolymerElement;
But when I curl for that resource, I find the old version of the resource:
➜  ice-code-editor git:(polymer) ✗ curl http://localhost:8080/packages/polymer/polymer_element.dart
// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library polymer.polymer_element;

import 'dart:async';
import 'dart:html';
import 'dart:mirrors';
import 'dart:js' as js;

import 'package:custom_element/custom_element.dart';
import 'package:mdv/mdv.dart' show NodeBinding;
import 'package:observe/observe.dart';
import 'package:observe/src/microtask.dart';
import 'package:polymer_expressions/polymer_expressions.dart';

import 'src/utils.dart' show toCamelCase, toHyphenedName;

/**
 * Registers a [PolymerElement]. This is similar to [registerCustomElement]
 * but it is designed to work with the `<element>` element and adds additional
 * features.
 */
void registerPolymerElement(String localName, PolymerElement create()) {

Much more follows...
Bother. It would seem that pub serve is reading pubspec.lock in order to figure out where to find its files—completely ignoring the symbolic links in the packages directory.

Manually manipulating symbolic links in the packages directory is already pretty far off the rails, but before abandoning the effort entirely, I try modifying the pubspec.lock file that is generated by pub install.

Unfortunately, when I try out the Pub server, it notices my modifications and overwrites them:
➜  ice-code-editor git:(polymer) ✗ pub serve
Dependencies have changed, installing...
Resolving dependencies......................
Dependencies installed!
Serving ice_code_editor on http://localhost:8080
Ah well, it was worth a try.

It is a bummer, but pubspec.yaml dependency hell is currently the only way that I can see to try out existing projects with new core-package changes.

Day #888

Friday, September 27, 2013

Patching and Using Core Dart Packages (the Wrong? Way)


My daughter is having difficulty doing cartwheels. She is much aggrieved by this for two reasons. First, I can do a cartwheel (quite gracefully I might add), which leads to the but-even-daddy-can-do-it objection. The second reason that this bothers her so much is that it is “embarrassing” that she is incapable of doing what so many of her friends can do. I consoled her by telling her that very few people can pull off cartwheels as well as I can. That did not seem to have quite the soothing effect that I anticipated, so I let her know that I embarrass myself publicly on a daily basis.

That seemed to do the trick. So now all that's left is to figure out how I will embarrass myself tonight. Trying to figure out how to apply and use the latest patch to Polymer.dart for attribute binding seems a fine candidate for the job.

Last night, I manually downloaded and applied a diff for a single file in a single Polymer.dart dependency. The patch for today spans several packages, so that trick, although it would work, would be a tad time-consuming. Instead I am going to apply the patch directly to the Dart source code.

Many packages that are only available from Dart Pub are, in fact maintained by the core Dart team alongside the core Dart libraries.

So I clone the repository from GitHub:
➜  repos  git clone https://github.com/dart-lang/bleeding_edge.git dart              
Cloning into 'dart'...
remote: Counting objects: 413160, done.
remote: Compressing objects: 100% (88516/88516), done.
remote: Total 413160 (delta 294342), reused 395016 (delta 276211)
Receiving objects: 100% (413160/413160), 577.79 MiB | 3.46 MiB/s, done.
Resolving deltas: 100% (294342/294342), done.
Next, I download the patch:
➜  dart git:(master) wget https://codereview.chromium.org/download/issue24149003_42001.diff
The patch is in git-am format, so I will give it a go with git-apply:
➜  dart git:(master) ✗ git apply --directory dart --check issue24149003_42001.diff
error: patch failed: dart/pkg/custom_element/lib/custom_element.dart:178
error: dart/pkg/custom_element/lib/custom_element.dart: patch does not apply
error: patch failed: dart/samples/third_party/todomvc/web/editable_label.html:3
error: dart/samples/third_party/todomvc/web/editable_label.html: patch does not apply
Hrm....

Well, hopefully the 3-way fallback option will get me close enough:
➜  dart git:(master) ✗ git apply --directory dart -3 issue24149003_42001.diff      
error: patch failed: dart/pkg/custom_element/lib/custom_element.dart:178
Falling back to three-way merge...
Applied patch to 'dart/pkg/custom_element/lib/custom_element.dart' cleanly.
error: patch failed: dart/samples/third_party/todomvc/web/editable_label.html:3
Falling back to three-way merge...
Applied patch to 'dart/samples/third_party/todomvc/web/editable_label.html' with conflicts.
U dart/samples/third_party/todomvc/web/editable_label.html
Yay! That is close enough. The only conflict is in the “todo” app, which I do not care about anyway.

So I modify pubspec.yaml from the project in which I am trying to use Polymer:
name: ice_code_editor
# ...
dependencies:
  # ...
  polymer:
    path: /home/chris/repos/dart/dart/pkg/polymer
  custom_element:
    path: /home/chris/repos/dart/dart/pkg/custom_element
  observe:
    path: /home/chris/repos/dart/dart/pkg/observe
And then a quick pub install to get all of my dependencies:
➜  ice-code-editor git:(polymer) ✗ pub install
Resolving dependencies.................................
Incompatible dependencies on 'custom_element':
- 'ice_code_editor' depends on it from source path
- 'polymer' depends on it from source hosted
Ew.

So, maybe it will not be a quick pub install. Not knowing what else to try, I modify the pubspec.yaml file in the dart repository to also use local paths:
name: polymer
dependencies:
  # ...
  csslib: any
  custom_element:
    path: /home/chris/repos/dart/dart/pkg/custom_element
  html5lib: any
  html_import: any
  logging: any
  mdv: any
  meta: any
  observe:
    path: /home/chris/repos/dart/dart/pkg/observe
  path: any
  polymer_expressions: any
  # ...
And that drags me down into dependency hell. Several of the other packages depend on observe so I have to edit the pubspec.yaml of each in turn. Eventually, I get far enough down that I have a successful pub install, but… yuck.

So I must be doing this wrong. Which means that I can definitely report back to my daughter that I have successfully embarrassed myself tonight. Yay!

I do have the patch and enough of the paths updated, so I may just go ahead and explore the changes in the proposed patch tomorrow. But hopefully someone will be kind enough to explain a better approach to linking such a large dependency fingerprint into a project.


Day #887


Thursday, September 26, 2013

Quick-Patching a Dart Package (Polymer.dart)


Thanks to the (early) power of Polymer.dart, I was able to create a custom tag for the ICE Code Editor:
<ice-code-editor src="embed_a.html" lineNumber="49"></ice-code-editor>
Which created an embedded version of the browser in a web page, with content loaded from embed_a.html and scrolled so that the very important line number 49 is at the top of the mini editor window:



I was especially excited that I was able to get the attribute reading working last night after lamenting just the night before that it was impossible with this early version of Polymer.dart. I am excited about the power of Polymer and it seems worth exploring further.

From last night's comments Charlie M indicates that I might be able to make at least two improvements. First off, the camel-cased lineNumber attribute on my custom <ice-code-editor> element is actually supposed to be line-number—it seems that Polymer will take care of converting to the camel-cased bound variable for me. Secondly, there may be some shenanigans that I can play to get attribute variable binding working without last night's hack.

I start addressing both by updating the Polymer template so that it accepts the hyphenated line-number attribute and uses the lineNumber bound variable:
<polymer-element name="ice-code-editor" attributes="src,line-number">
  <template>
    <h1>The src is "{{src}}" ({{lineNumber}})</h1>
    <content></content>
  </template>
  <script type="application/dart" src="ice_polymer.dart"></script>
</polymer-element>
Then, in the backing ice_polymer.dart code, I add some default values for my attributes:
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:ice_code_editor/ice.dart' as ICE;

@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src = '';
  @observable int lineNumber = 0;

  void created() {
    super.created();
    // src = host.attributes['src'];
    // lineNumber = int.parse(host.attributes['line-number']);

    // Setup ICE like normal here...
  }
}
I also comment out last night's hack that bound the observed variables, src and lineNumber, to the attributes from the custom element.

Instead, I go back to where the custom element is used and try to supply attributes with expressions instead of values:
<ice-code-editor src="embed_a.html" line-number="{{49}}"></ice-code-editor>
Unfortunately, that does not work:



Try as I might, no variation of expression, regular attribute value, or initial values in the IceCodeEditorElement class seems to have any effect. With the current version of Polymer.dart, last night's hack (or something similar) would appear to be the only way to access attributes.

At least currently. There are two ongoing code discussions that look to be trying to solve the problem. The older of the two looks to have been a first pass at the problem. The second review seems to be nearing a consensus as to the actual approach.

I am going to give the first approach a whirl tonight. It touches on only a single file in a single package whereas the second touches on multiple files in three packages. Since I am using Dart, modifying a single dependency is a breeze. I copy the Dart Pub package from my local cache into my repos directory, download the patch, and apply it:
➜  repos  cp -r ~/.pub-cache/hosted/pub.dartlang.org/polymer-0.7.5 .      
➜  repos  cd polymer-0.7.5 
➜  polymer-0.7.5  wget https://chromiumcodereview.appspot.com/download/issue22417004_1.diff
➜  polymer-0.7.5  patch -p1 < issue22417004_1.diff
patching file lib/polymer_element.dart
Hunk #1 succeeded at 120 (offset 3 lines).
Back in my ICE project, I update the project's pubspec.yaml to depend on my modified, local version of Polymer:
name: ice_code_editor
# ...
dependencies:
  # ...
  polymer:
    path: /home/chris/repos/polymer-0.7.5
After a quick pub update and a restart of pub serve, I find that the patch did the trick:

With this particular patch, the custom tag's attributes do not need to be expressions—regular values work just fine:

<ice-code-editor src="embed_a.html" line-number="49"></ice-code-editor>
I do see why this patch was just the starting point for discussion. I find that it does not work if I fail to supply an initial value for the line number bound variable:
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src = '';
  @observable int lineNumber;
  // ...
}
That was fun. The ability to quick-patch a single package like that is a killer feature for Dart. I am glad to have a grip on where things stand on getting bound attributes working in Polymer.dart. I could probably leave it at this, but I may give the three-package patch a whirl tomorrow. Just for the fun of it.

Day #886

Wednesday, September 25, 2013

Reading Attributes from Polymer Tags


After 884 consecutive days of blog posting and 4 resulting books, I begin to fancy myself a testament to the power of small chunks of perseverance. Lucky for me and, more importantly, the size of my head, I have nights like last night to keep me humble. Well, humble is probably stretching it. What's the word for almost declaring myself super-awesome-hacker-extraordinaire, but realizing I might be long on one or two adjectives? Whatever that word is, that's me.

But in all seriousness, I was thwarted in my efforts to create a custom element for an embedded version of the ICE Code Editor. I tried to use Polymer.dart for the effort, but found that I could not bind attributes in the custom element to instance variables in the backing PolymerElement class. Frustrated, I went so far as to initially start tonight's post with:
I find it maddening that there is no way to access the original tag from Polymer.dart code. At the same time, I suspect that this inability speaks to the very heart of Polymer—binding data with elements and all. But for the beginner, it's freaking annoying.
And then I took a moment and a day to think...

And thinking, I realized that I had not done this. I never tried to access the original attributes directly from the page in which my custom element is used:
<ice-code-editor src="embed_a.html" line-number="49"></ice-code-editor>
Actually, before doing anything else, I switch from line-number (previously implemented as a data- attribute before the switch to Polymer). Instead, I will use lineNumber, which will work as a Polymer variable:
<ice-code-editor src="embed_a.html" lineNumber="49"></ice-code-editor>
The Polymer element file remains nearly the same as last night:
<polymer-element name="ice-code-editor" attributes="src,lineNumber">
  <template>
    <h1>The src is "{{src}}"</h1>
    <content></content>
  </template>
  <script type="application/dart" src="ice_polymer.dart"></script>
</polymer-element>
And the backing code referenced in the src attribute still retains the same outline:
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'package:ice_code_editor/ice.dart' as ICE;

@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src;
  @observable int lineNumber;

  void created() {
    super.created();
    // build element here...
  }
}
I will do the bulk of the work in the created() method which is called by Polymer after the custom element is… created.

Tonight's stroke of genius comes courtesy of realizing that my custom IceCodeEditorElement class is extending PolymerElement, which itself extends CustomElement. The thing about CustomElement is that it has an attributes property:
    src = host.attributes['src'];
    lineNumber = int.parse(host.attributes['lineNumber']);
With that I can load the source file and set the line number as needed:
  void created() {
    super.created();

    src = host.attributes['src'];
    lineNumber = int.parse(host.attributes['lineNumber']);

    var container = new DivElement()
      ..id = 'ice-${this.hashCode}'
      ..style.width = '600px'
      ..style.height = '400px';

    host.children.add(container);

    var editor = new ICE.Editor('#${container.id}');

    HttpRequest.getString(src).then((response) {
      editor.content = response;
      editor.editorReady.then((_)=> editor.lineNumber = lineNumber);
    });
  }
After reading the src and lineNumber attributes, I assign them to the observed instance variables of the same name. This will allow the template to include those values if it needs them. In the template above, I just use src as part of the heading (more as a proof-of-concept than for any practical reason). Once I have those values, I create a new instance of Editor. Then I make a HTTP request for the resource specified by src. Once the content of that request comes back, I assign it to the editor's content property. The last thing that I do is to scroll the editor to the proper line number.

And, with that very simple change in place, I have an embedded version of ICE working, thanks to Polymer:



That is a full featured, custom element. And if I had just paid a little bit more attention yesterday, I might have realized how easy it was to create it.

Someday I might achieve that level of awesomeness. Until then, I can only put together my next, small chunk of learning. Tomorrow.


Day #885

Tuesday, September 24, 2013

Getting Started with Polymer (.dart)


I messed around with Web UI components a while back. Since they have been replaced by Polymer, and since I have need of a custom component, this seems an opportune time to play around with both.

A week or so ago, we got faux-<script> support added to ICE Code Editor. The tag looked like:
<script type="text/ice-code" src="embed_a.html" data-line-number="49"></script>
A little bit of Dart code scanned the HTML for text/ice-code <script> tags, replacing them with embedded versions of the code. This seems an ideal application of Polymer.

Instead of faux-<script> tags, I would like a real <ice-code-editor> tag along the lines of:
<ice-code-editor src="embed_a.html" line-number="49"></ice-code-editor>
So...

First, I add polymer as a dependency for the ICE:
name: ice_code_editor
# ...
dependencies:
  # ...
  polymer: any
Then install the newly added dependency:
➜  ice-code-editor git:(embedded) ✗ pub install
Resolving dependencies.................................................................................
Downloading polymer 0.7.3+1 from hosted...
Downloading barback 0.7.3+1 from hosted...
Downloading csslib 0.7.3+1 from hosted...
Downloading shadow_dom 0.7.3+1 from hosted...
Downloading polymer_expressions 0.7.3+1 from hosted...
Downloading observe 0.7.3+1 from hosted...
Downloading yaml 0.7.3+1 from hosted...
Downloading html5lib 0.7.3+1 from hosted...
Downloading html_import 0.7.3+1 from hosted...
Downloading mutation_observer 0.7.3+1 from hosted...
Downloading source_maps 0.7.3+1 from hosted...
Downloading utf 0.7.3+1 from hosted...
Downloading mdv 0.7.3+1 from hosted...
Downloading custom_element 0.7.3+1 from hosted...
Dependencies installed!
Polymer requires two things to work. The HTML <polymer-element> template, which I create in a separate ice_code_editor.html file:
<polymer-element name="ice-code-editor" attributes="src,line-number">
  <template>
    <h1>The src is {{src}}</h1>
    <content></content>
  </template>
  <script type="application/dart" src="ice_polymer.dart"></script>
</polymer-element>
I then create the referenced ice_polymer.dart file as:
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'package:ice_code_editor/ice.dart' as ICE;

@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src;

  void created() {
    super.created();

    var container = new DivElement()
      ..style.width = '600px'
      ..style.height = '400px';

    print('here: ${src}');
    print(host.text);
    host.children.add(container);
    // create ice here
  }
}
The observable stuff should give me access to the necessary src and line-number attributes. Except they don't.

When I try to use my custom element from my main web page:
<head>
  <link rel="import" href="ice_code_editor.html">
  <script src="packages/polymer/boot.js"></script>
</head>
<body>

<p>
asdf asdf as df sadf as df asdf
</p>

<ice-code-editor src="embed_a.html" line-number="49"></ice-code-editor>

<p>
asdfsadfasd sadf asdf  sdf asdf a sdfsfd
</p>

</body>
I get an empty value for the src.



This is apparently a known bug in Polymer.dart. Unfortunately, it is something of a show stopper for me in this case.

I can kinda-sorta make it work by embedding the attributes in the content of my custom tag:
<ice-code-editor>src="embed_a.html" line-number="49"</ice-code-editor>
I can then parse that information out in the create() method of my PolymerElement:
@CustomTag('ice-code-editor')
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @observable String src;

  void created() {
    super.created();
    // ...
    Map attrs = {};
    host.text.split(" ").forEach((attr) {
      var pair = attr.split('=');
      attrs[pair[0]] = pair[1];
    });
    // Setup ICE here...
  }
}
But that's pretty ugly. Bother.

Somewhat frustrated, I call it a night here. I am unsure if there is much point in pushing further with this until that bug is resolved. It seems close to what I want, but it may be best to wait for resolution of that issue before making any final decisions.


Day #884

Monday, September 23, 2013

Tracking Down Internal Dartium Errors


I have been having myself some fun over the past few days writing Dart unit tests for a Backbone.js application (that's right, Dart tests for a JavaScript application). I think the fun is over.

I upgraded to the latest Dart VM last night (0.7.3.1_r27487) and have been getting nothing but sad tabs ever since:



(if someone wants to reproduce, it is e0b18ea in the dart-testing branch of Funky-Backbone.js-Calendar)

Interestingly, the tests still seem to work if I close the JavaScript console, run the tests, then re-open the JavaScript console:



I am not too fussed by this. After all, this entire experiment has been fairly insane. Fun, to be sure, but I doubt that there are immediate practical applications. I still believe that Dart's built-in testing is superior to anything that exists in JavaScript-land, but there are too many volatile parts involved at the moment: Dart, js-interop, and Dart's unittest.

There is also the odd discrepancy in behavior between events in Dart and JavaScript. As I found last night, JavaScript does not see Dart click events. Given the oddness of the sad-tab tonight, I am going to let that one lie. Before moving onto fresh ground, there is one other strange behavior that has been bugging me: I cannot query for the calendar date elements.

In my tests, I have been forced to find elements with iterators:
      var cell = queryAll('td').
        where((el)=> el.id == fifteenth).
        first;
Instead, I ought to be able to simply query for an element with that ID:
      var cell = query('#${fifteenth}');
But this has been resulting in my most favoritest Dart error of all—the dreaded internal Dartium error:
ERROR: the initial view populates the calendar with appointments
  Test failed: Caught SyntaxError: Internal Dartium Exception
  ../../../../../../mnt/data/b/build/slave/dartium-lucid64-full-trunk/build/src/out/Release/gen/blink/bindings/dart/dart/html/Document.dart 123:128  Document.query
  dart:html 448:49                                                                                                                                   query
  ../test.dart 60:23
(I even got that before the crashy Dartium+unittest combo)

So what gives here? The element is clearly there—if I query all of the table cell elements on the page and iterate through them, I can find the element with the target ID. But I cannot query for said element.

To explore this particular issue, I break it down into a very small web test page and associated script. In the test page, I include a simple JavaScript script that creates an element. I also include a number of elements that are entered by hand:
<html>
<head>
  <title>Test JS Elements</title>
  <script src="packages/browser/dart.js"></script>
  <script src="packages/browser/interop.js"></script>
  <script type="application/dart" src="main.dart"></script>
</head>
<body>
  <div id="2013-09-22"></div>
  <div id="d2013-09-21"></div>
  <div id="test"></div>
</body>
<script type="text/javascript">
  var newDiv = document.createElement("div");
  newDiv.id = 'j2013-09-23';
  document.body.appendChild(newDiv);
</script>
</html>
Quick experimentation ends up proving that my problem is the ID attribute. If the ID on which I am querying does not contain a character (I had been using ISO 8601 dates in the Backbone application), then I get the error:
// main.dart
import 'dart:html';
import 'package:js/js.dart' as js;

main() {
  var el = query('#2013-09-22');
  print(el);
}
Regardless of what the spec say, browsers allow the use of IDs with just numbers and dashes and these make the most sense in a calendar application. Regardless, an internal Dartium crash is not a helpful error message, so it's off to the bug reporting machine with me! Still, I am happy to have that minor mystery solved.


Day #883

Sunday, September 22, 2013

Full-Stack Backbone.js Testing with Dart


Over the last few days, I have more or less gotten Dart's awesome unit testing working with a Backbone.js application. Thanks to the magic of Dart's js-interop, I have a real test running against the Funky Calendar from Recipes with Backbone. And after last night, I have setup and teardown working. For my latest feat of insanity (and I mean that, there's no real reason to do this) I will attempt testing the creation of appointments in the Backbone.js calendar.

If there is a deficiency to testing Dart applications, it is the inability to stub HTTP requests. Although this makes unit testing tougher, it has the advantage of forcing developers into a more full-stack frame of mind. Towards that end, I have a “real fake” test server (plummbur-kruk) backing my REST-like Backbone application in test. In the test setup, I am already creating a calendar appointment for the current month:
    setUp((){
      document.head.append(new BaseElement()..href = Kruk.SERVER_ROOT);;

      el = document.body.append(new Element.html('<div id=calendar>'));

       var doc = '''
         {
           "title": "Get Funky",
           "description": "asdf",
           "startDate": "${fifteenth}"
         }''';

      return Future.wait([
        Kruk.create(doc),
        Kruk.alias('/widgets', as: '/appointments')
      ]);
    });
With that setup in place, can start my Backbone application and eventually check my expectation that the appointment on the 15th of the month is included in the app:
expect(cell.text, matches("Get Funky"));
Tonight, I would like add a new test that clicks on particular date, enters appointment information, clicks the save button, and finally verifies that the appointment is now included on the calendar.

I start with the usual Backbone test thing (though with a Dart js-interop twist) by creating an instance of the Backbone application and ensuring that Backbone history is running properly:
    test("can create new appointments", (){
      new js.Proxy(js.context.Cal, query('#calendar'));
      js.context.Backbone.history.loadUrl();
      // ...
    });
Next, I find the table cell that contains the 14th of the month and click on it:
    test("can create new appointments", (){
      // ...
      var cell = queryAll('td').
        where((el)=> el.id == fourteenth).
        first
          ..click();
      // ...
    });
This should result in a popup dialog with a few appointment fields. I fill in those appointment values next:
    test("can create new appointments", (){
      // ...
      query('#calendar-add-appointment')
        ..query('input.title').
          value = 'Test Appointment'
        ..query('input.description').
          value = 'asdf';
      // ...
    });
Finally, I click the OK button in the popup:
    test("can create new appointments", (){
      // ...
      query('.ui-dialog-buttonset').
        query('button').
        click();
      // ...
    });
With that, I should be able to check my expectation that the fourteenth of the month now contains an appointment created in my test (after a brief wait for all of the Backbone events to propagate):
    test("can create new appointments", (){
      // ...
      new Timer(
        new Duration(milliseconds: 10),
        expectAsync0((){
          expect(cell.text, matches("Test Appointment"));
        })
      );
    });
But, for some reason, that fails:
FAIL: the initial view can create new appointments
  Expected: match 'Test Appointment'
    Actual: '14'
All that is in the table cell for the 14th is the day of the month. If I remove the teardown step to see the status of the (un-styled) popup dialog, I find that everything seems to have worked except for the clicking of the button:



And oddly enough, if I use jQuery (which is available to me via js-interop), I am able to click the button:
      js.context.jQuery('.ui-dialog-buttonset button').click();
Which results in a successful test:
PASS: the initial view can create new appointments
I am at a loss to explain why the click event in jQuery works, but it does not work from Dart. Perhaps there is an additional event property that is being added by jQuery? I have a working test, but would very much like to have the test in pure Dart (or at least have a idea why that won't work). So I will likely use that as an excuse to continue investigation tomorrow…


Day #882


Saturday, September 21, 2013

The Neighbor's Shoe


What could be finer that one Dart unit test of a Backbone.js application?

Yup, two.

I remain skeptical of this overall approach. Testing a JavaScript application with Dart is crazy, right? Well, maybe not too crazy. Dart's built-in unittest library is pretty awesome and there are already some supporting libraries sprouting up to make it even nicer. Aside from the inability stub HTTP requests, Dart testing is superior to any JavaScript testing solution. I do not believe that to be an exaggeration.

Anyhow, I am going to start by duplicating my one test rather than creating a second useful test. This will minimize the moving parts, helping to isolate problems. The working test is:
    test("populates the calendar with appointments", (){
      new js.Proxy(js.context.Cal, query('#calendar'));

      var cell = queryAll('td').
        where((el)=> el.id == fifteenth).
        first;

      new Timer(
        new Duration(milliseconds: 10),
        expectAsync0((){
          expect(cell.text, matches("Get Funky"));
        })
      );
    });
It uses Dart's js-interop to instantiate the Backbone application (Cal). It looks through all of the calendar elements on the page for the 15th of the month and finally checks the expectation that the appropriate “funky” appointment is on the calendar. From last night, the 10 millisecond delay was the magic sauce that allowed the application to query the test backend server, drawing the results on the screen.

I already have a teardown function that removes the Backbone application and associated elements. So I make a copy of the test, changing only the test description by adding “(copy)”:
    test("populates the calendar with appointments (copy)", (){
      // Exact same test here...
    });
Naturally that fails:
PASS: the initial view populates the calendar with appointments
FAIL: the initial view populates the calendar with appointments (copy)
  Expected: match 'Get Funky'
    Actual: '15'
C'mon Dart! What gives?

Well, maybe this isn't Dart. Instead, there is the funky nature of Backbone URL routing that requires routing history to be started (to manage push state and the like). My Backbone application does that:
  // ...
  new Routes({application: application});
  try {
    Backbone.history.start();
  } catch (x) {
    console.log(x);
  }
  // ...
And it works just fine when the application is run once. Under normal circumstances, running once is just fine—a Backbone application does not need to start twice in a browser. Under test however, Backbone applications get run many, many times. Or twice when you're messing around with Dart testing. This is the reason for the try-catch in my existing application—to prevent exceptions under test when Backbone.history.start() is called more than once.

That's all well and good, but if I am going to test an application that relies on history—as this one does—I need some way to reset history each time the application is instantiated. In my Jasmine tests, I had been doing this with Backbone.history.loadUrl(). In Dart, I can access that method via the main context proxy:
    test("populates the calendar with appointments (copy)", (){
      js.context.Backbone.history.loadUrl();
      new js.Proxy(js.context.Cal, query('#calendar'));
      // ...
    });
But when I try the tests now, I get a very unhelpful failure:
ERROR: the initial view populates the calendar with appointments (copy)
  Test failed: Caught TypeError: Cannot call method 'replace' of undefined undefined:1
Here, I think, is a definite problem with using Dart to test JavaScript—the error message stinks. There is no stack trace. The line number—undefined:1—is useless. I have to infer where the problem is based on the message alone.

I would remind readers at this point that no one thinks using Dart to test JavaScript applications is a good idea. None of this should be taken as an indictment of Dart, Dart testing or Backbone. What I am doing is silly. But sometimes, you find useful stuff when doing silly things. Anyhow…

I am able to figure out the problem eventually. I had gotten the order of Backbone.history.loadUrl() and instantiation of my application wrong. Specifically, the URL needs to be performed after the application is running:
    test("populates the calendar with appointments (copy)", (){
      new js.Proxy(js.context.Cal, query('#calendar'));
      js.context.Backbone.history.loadUrl();
      // ...
    });
With that, I have two Dart tests passing that describe my Backbone application:
unittest-suite-wait-for-done
PASS: the initial view populates the calendar with appointments 
PASS: the initial view populates the calendar with appointments (copy)
All 2 tests passed.
unittest-suite-success
The loadUrl() order error was a pretty silly mistake to make. I could have just as easily made the same mistake testing in JavaScript that I did in Dart. A more descriptive stack trace or error message would almost certainly have helped me understand the problem sooner.

Still, I love Dart testing enough that I am not 100% ready to drop the idea of using Dart to test JavaScript applications. I admit that I am a dog with the neighbor's shoe here. I should not have the shoe and I know this. But man, is it fun to rip into it. So I'll probably continue gnawing on it tomorrow.


Day #881

Friday, September 20, 2013

Insanity Day 2: Testing Backbone with Dart


Sometimes you just have to try crazy stuff. Yesterday I added Dart unittest support into my Backbone.js-powered calendar application.

Today, I start with a failing test:
    test("populates the calendar with appointments", (){
      // js.context.Backbone.history.loadUrl();
      new js.Proxy(js.context.Cal, query('#calendar'));

      var _id = iso8601.format(fifteenth);
      var cell = queryAll('td').
        where((el)=> el.id == _id).
        first;

      print('Contents (${_id}): ${cell.text}');
      expect(cell.text, matches("Get Funky"));
    });
In there, I create an instance of the Backbone application, Cal, via Dart's js-interop. Then find the 15th of the month in the generated calendar. Finally, I check my expectations—that the calendar contains the correct, “funky” appointment.

Amazingly, that test actually runs:



Sure, it fails, but it fails in a useful way—with the expectation that a record that should exist on the 15th of the month is not on the Backbone calendar.

Looking closer at the network tab of Chrome's Developer tools, it seems that my “real fake” test server is missing a CORS allowed request header:



That must be a jQuery (used by Backbone) thing. Since my test is running from a file:// URL and my “real fake” test server is running on port 31337 of my localhost, I need that header. Fortunately, that is easy enough—made easier since I am using Dart, naturally. I switch my project from depending on the published version of plummbur_kruk (the “real fake” server), to my local repository:
name: funky_calendar
dependencies:
  unittest: any
  plummbur_kruk:
    path: /home/chris/repos/plummbur-kruk
  intl: any
  js: any
After a quick pub update, my Dart packages are switched over. In my local plummbur_kruk, I add another Access-Control-Allow-Headers:
      HttpResponse res = req.response;
      res.headers
        ..add('Access-Control-Allow-Origin', 'null')
        ..add('Access-Control-Allow-Headers', 'Content-Type')
        ..add('Access-Control-Allow-Headers', 'X-Requested-With')
        ..add('Access-Control-Allow-Methods', 'GET,DELETE,PUT');
Now, when I run my test, I get just the test failure and no network errors:
FAIL: the initial view populates the calendar with appointments
  Expected: match 'Get Funky'
    Actual: '15'
This turns out to be a simple matter of timing. The expectations in my test are being checked before the Backbone application has a change to layout all of its elements. The easiest fix is to add a minimal delay before checking the expectation:
    test("populates the calendar with appointments", (){
      // js.context.Backbone.history.loadUrl();
      new js.Proxy(js.context.Cal, query('#calendar'));

      var _id = iso8601.format(fifteenth);
      var cell = queryAll('td').
        where((el)=> el.id == _id).
        first;

      print('Contents (${_id}): ${cell.text}');

      new Timer(
        new Duration(milliseconds: 10),
        expectAsync0((){
          expect(cell.text, matches("Get Funky"));
        })
      );

    });
And with that, I actually have a passing test:
unittest-suite-wait-for-done
Contents (2013-09-15): 15
PASS: the initial view populates the calendar with appointments

All 1 tests passed.
unittest-suite-success
The tests pass and (if I comment out the teardown), I can even see the unstyled calendar on the test page:



Insanely enough, I can run a legitimate test for Backbone code with Dart unit testing. The benefits are obvious: easier setup, better support for running under continuous integration, nicer test syntax, better async support. There are more questions than answers at this point, but I'm pretty sure I have the makings of another recipe for Recipes with Backbone on my hands.

I kid! Or do I…?

Day #880

Thursday, September 19, 2013

Crazy Testing of Backbone.js Apps with Dart


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: any
Since 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       
I am not quite sure what is going on here. That error, of course, is because I forgot to include the 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


Wednesday, September 18, 2013

Testing Canvas in Dart


While working through some of the later chapters in Dart for Hipsters, I found some code that really was not tested. Better yet, I know why it is untested: I don't know how. Yay! Fresh earth.

The Dart code in question is part of a whirlwind tour at the end of the book. Specifically, I have a very simple canvas demonstration:



The code draws a simple square, fills it, and attaches keyboard handlers to move the square around the screen. I already know that I cannot test the keyboard events. At least not yet. But I can still test something. In fact, if I had been testing, or at least performing simple code analysis, I would have noticed that I still have the old-style on.keyDown.add() event handling:
  document.
    on.
    keyDown.
    add((event) {
      String direction;

      // Listen for arrow keys
      if (event.keyCode == 37) direction = 'left';
      if (event.keyCode == 38) direction = 'up';
      if (event.keyCode == 39) direction = 'right';
      if (event.keyCode == 40) direction = 'down';
      // ...
    });
I get everything working by switching to the new stream-based event handling:
  document.
    onKeyDown.
    listen((event) { /* ... */ });
But I have nothing in place to prevent regressions should the language continue to evolve.

This turns out to be fairly easy with Dart's unittest:
import 'package:unittest/unittest.dart';
import 'dart:html';

import '../web/main.dart' as Canvas;

main () {
  group("[canvas]", (){
    var canvas;

    setUp((){
      canvas = new CanvasElement();
      document.body.append(canvas);
    });

    tearDown(()=> canvas.remove());

    test("code runs", (){
      expect(Canvas.main, returnsNormally);
    });
  });
}
Here, I import the main.dart entry point for my canvas experiment and manually run the main() entry point function—expecting it to return normally. A bit of setup is required to add an expected <canvas> tag (along with matching teardown code). But really, very little is required for a pretty useful test.

It even works from the command-line thanks to the magic of Chrome's content_shell:
➜  code git:(master) ✗ content_shell --dump-render-tree index.html
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: [canvas] code runs
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 1 tests passed.
If I intentionally break the code (by reverting my stream fix), I get the failure that would have helped me earlier:
➜  code git:(master) ✗ content_shell --dump-render-tree index.html
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: FAIL: [canvas] code runs
  Expected: return normally
    Actual: <Closure: () => dynamic from Function 'main': static.>
     Which: threw NoSuchMethodError:<Class 'Events' has no instance getter 'keyDown'.

  NoSuchMethodError : method not found: 'keyDown'
  Receiver: Instance of 'Events'
  Arguments: []>

  package:unittest/src/simple_configuration.dart 140:7
  package:unittest/src/simple_configuration.dart 15:28
  package:unittest/src/expect.dart 117:9
  package:unittest/src/expect.dart 75:29
  ../html5/test/test.dart 20:13
My code event passes type analysis, which I perform automatically on any tested code in the book with dartanalyzer in my test_runner.sh:
# ...
# Static type analysis
echo
echo "Static type analysis..."

results=$(dartanalyzer test.dart 2>&1)
echo "$results"
if [[ "$results" == *"[error]"* ]]
then
    exit 1
fi
# ...
That is all well and good, but is it possible to test some of the innards of the <canvas> element after the initial screen is drawn by main()? As can be seen from the screenshot above, the “player” starts near the top-left of the screen. In fact, the default is specified in the Player constructor:
class Player {
  int x, y;
  int width = 20, height = 20;
  Player() {
    x = width~/2;
    y = height~/2;
  }
  // ...
}
The x-y origin of the starting position is then (20/2, 20/2), or (10, 10). Given that the width and height of the box is 20×20, the box ends at (30, 30). Since the “player” box is the last thing drawn in the <canvas> element, I can have an expectation that the point (30, 30) is in the current path:
    test("player is drawn", (){
      Canvas.main();
      var context = canvas.getContext('2d');
      expect(context.isPointInPath(30, 30), isTrue);
    });
And that passes!
PASS: [canvas] player is drawn 
If I change the expectation to (31, 31), my test fails:
FAIL: [canvas] player is drawn
  Expected: true
    Actual: <false>
So not only do I have a test that will catch small changes in the language that might affect my <canvas> code, I also have a test that will verify that my drawing works properly. And it even works from the command line.


Day #878

Tuesday, September 17, 2013

A Test Not Run is a Worthless Test


I must have detached the bulk of my Dart for Hipsters tests from the main test suite back when I first started on the latest rewrite. Since that was such a long time ago, I have since forgotten, leaving me with a bunch of tests that are not running. In other words, I have a bunch of worthless tests.

As I work through each chapter's worth of tests, hooking the old tests back up, I am pleasantly surprised when they pass. At the same time, I am eager for failures because that means that something may have changed—something may be new to me. Tonight, as I was working through the chapter on dynamic language features, I ran into some problems with my noSuchMethod() code.

Not wanting a repeat of last night's debacle in which I very nearly repeated a post, tonight I search for and find the original post on this code. That does not include the somewhat recent switch from invocation “mirrors” to a plain-old invocation objects. That introduction article does a pretty good job of explaining the errors that I am seeing in my tests:
ERROR: [noSuchMethod] can pass parameters to noSuchMethod in superclass
  Test failed: Caught NoSuchMethodError : method not found: 'bar'
  Receiver: top-level
  Arguments: [2, 2]
  ../varying_the_behavior/test/superclass_no_such_method.dart 13:5                                                                          A.noSuchMethod
  ../varying_the_behavior/test/superclass_no_such_method.dart 28:30                                                                         C.noSuchMethod
  ../varying_the_behavior/test/superclass_no_such_method.dart 46:19                                                                         run..
  package:unittest/src/test_case.dart 111:30  
My code is actually reaching the throw statement in my noSuchMethod() even though my test is trying to invoke the bar() method:
class A {
  noSuchMethod(args) {
    if (args.isMethod && args.memberName == "bar") {
      return 2 * args.
        positionalArguments.
        reduce(0, (prev, element) => prev + element);
    }

    throw new NoSuchMethodError(
      this,
      args.memberName,
      args.positionalArguments,
      args.namedArguments
    );
  }
}
The problem is that memberName is no longer a string (“bar”). It is now a Symbol version of that string. It is interesting what a language sometimes has to do to itself when it has to compile into a another language. In this case, my Dart test code needs to work when compiled down to JavaScript:
    test('can access noSuchMethod in superclass when defined in subclass', (){
      var c = new C();
      expect(()=> c.bar(2, 2), returnsNormally);
    });
The problem, as the announcement article explains so well, is not so much that the above code can be compiled to JavaScript. Rather the problem is that the above test code might get compiled into minified JavaScript. In other words, the bar() method might get minified as a() in which case a string-based memberName would return “a” when my noSuchMethod() definition supports only “bar.”

Enter Symbol, which is treated differently in by the dart2js compiler. To put that to use in my noSuchMethod() definition, I compare memberName, which is a Symbol, to a Symbol object that I create:
class A {
  noSuchMethod(args) {
    if (args.isMethod && args.memberName == const Symbol("bar")) {
      return 2 * args.
        positionalArguments.
        fold(0, (prev, element) => prev + element);
    }
    // ...
  }
}
That is all that I need to do in order to make my test pass.

I am not quite done, though. Happily the invocation announcement article also includes a way to clean up my messy, six line throw-no-such-method throw statement. The bar() method is the only method that I support via noSuchMethod() in this class, so when something else is called, I need to raise an error which I had been doing with:
class A {
  noSuchMethod(args) {
    if (args.isMethod && args.memberName == "bar") { /* ... */ }

    throw new NoSuchMethodError(
      this,
      args.memberName,
      args.positionalArguments,
      args.namedArguments
    );
  }
}
Mercifully, there is a better way—by invoking noSuchMethod() on super:
class A {
  noSuchMethod(args) {
    if (args.isMethod && args.memberName == const Symbol("bar")) { /* ... */ }

    return super.noSuchMethod(args);
  }
}
In other words, let Object, or whatever other superclass might be involved, deal with the args Invocation object. Yay! Much nicer.

I cannot say that I am 100% sold on the idea of Symbol. It helps greatly to understand that the need for Symbol comes from the need to support JavaScript, but that's the problem. It made little sense to me when skimming the documentation what purpose the Symbol class served. And really, its only purpose seems to be to make JavaScript compiling work. Perhaps someday Dart will throw in a little Ruby-like syntactic sugar for defining symbols to make the benefit a little more obvious.

Until then… the tests for another chapter are passing!


Day #877

Monday, September 16, 2013

An Unexpected Follow-Up to Testing Dart Isolates


As I work through the latest rewrite of Dart for Hipsters, I occasionally come across untested code. That's just silly.

The latest untested is in the relatively brief section on isolates in Dart. At this point, I have not run that code since the last edition earlier this year so I would be surprised if the code still runs…

Which it does.

It would seem that the isolate API is fairly stable in Dart. The example in the book is fairly contrived, though in my defense I could not bring myself to create one more Fibonnaci number calculator. My example calculates the Doomsday for a given year. As an aside, I had no idea that same person responsible for inflicting the Game of Life on unsuspecting unconferences everywhere also came up with the Doomsday rule. I can't escape him.

Anyhow, the code spawns an isolate, calls into it with a year, and then waits for the response in a future:
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
  });
}
As mentioned, that just works:
➜  code git:(master) ✗ dart isolates/main.dart
Certain doom awaits...
Doom in 2013 is on a Thurs.
Happily, the only question left me—at least for this section—is how to test.

And wow. As I am getting ready to decide where to put my tests, I find that I have already done this. Good grief. You know that you blog too much when you don't realize that you have already covered a topic.

Anyhow, the reason that I thought this area was untested was because the tests for this chapter were not included in the book's test suite. I do so and wind up fixing a few of the future & completer tests. The isolate function, which tests spawning an isolate in the same way that my main() entry point does, still passes:
    test('can send back replies', (){
      SendPort sender = spawnFunction(Isolate.findDoom);
      expect(sender.call(2013), completion("Thurs"));
    });
That is a pretty test. I create an isolate with spawnFunction(), then I expect that calling it with 2013 will result in a future completion with the value of "Thurs." That's good stuff.

But what if I want to test the actual main() function that is included in the book? That I have not yet tested. To do so, I return the result of the then() method in main():
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  return sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
  });
}
I am not 100% sure that I will keep that because it might be a little awkward to explain the return statement in the narrative of the book. Well, that and it does not quite work. If I try to test it with:
    test('can perform calculations in an isolate', (){
      expect(Isolate.main(), completion("Thurs"));
    });
Then I get a null value failure:
FAIL: [isolates] can send back replies
  Expected: 'Thurs'
    Actual: <null>
     Which: <null>is not a string
I had expected that, since my then() does not return a value, that the completer would complete with the original value. It does not and I have to explicitly return a value:
main() {
  SendPort sender = spawnFunction(findDoom);

  print('Certain doom awaits...');

  var year = 2013;
  return sender.call(year).then((message) {
    print("Doom in $year is on a $message.");
    return message;
  });
}
I definitely will not be including two return statements in the narrative of the book just to satisfy my tests—even though my tests are satisfied:
PASS: [isolates] can send back replies
PASS: [isolates] can perform calculations in an isolate
Still, I would like to be able to test that main() function. I may pick back up with this tomorrow to see if I can get a more complete test covering this particular code. For now, I am happy to have my isolates and futures testing pulled into the main test suite of the book. Progress!





Day #876