Sunday, November 24, 2013

Testing Polymer.dart Web Components

Tonight, I try testing the Dart port of my Boostrap-based pricing panels Polymer component:

The first thing that testing tells me is that "assets" is the wrong place for yesterday's Polymer.dart definition files. In the application code, I could reference them as:
    <!-- Load component(s) -->
    <link rel="import" href="/assets/pricing_panels/pricing-plans.html">
    <link rel="import" href="/assets/pricing_panels/pricing-plan.html">
But, unless I want to run a web server every time I run my tests, that /assets path will not work—not because of the leading, absolute path slash, but because assets requires a Dart pub web server or build. I am not fussed by this. It was not clear to me that these mostly-HTML files were assets or code. Now, I know.

After moving them into the lib directory along with the backing Dart code, the only thing remaining in asset is the Bootstrap CSS, which feels right:
tree asset lib
└── bootstrap.min.css
├── pricing_plan.dart
├── pricing-plan.html
├── pricing_plans.dart
└── pricing-plans.html
Another thing that is going to cause me problems is Polymer.dart's need to import files itself. Since the test file is typically being loaded from a file:// URL, I need to restart Dartium with --allow-file-access-from-files so that I do not get cannot-load-file errors:
$ chrome --allow-file-access-from-files
As for the test files, I start with the web page context, which is a stripped-down version of the application page. In it, I need code that imports the Polymer definitions via <link> tags, a reference to my test code, and a faux-fixture <pricing-plan> tag:
  <!-- Load component(s) -->
  <link rel="import" href="packages/pricing_panels/pricing-plans.html">
  <link rel="import" href="packages/pricing_panels/pricing-plan.html">

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

  <pricing-plan>plan content</pricing-plan>
I am including the <pricing-plan> plan tag here rather than dynamically generating it in my tests to avoid dealing with Dart's HTML sanitizing (for now).

One of the things missing from that web page context is the initialization of Polymer, which is normally done via:
<script type="application/dart">export 'package:polymer/init.dart';</script>
To avoid duplicate main() entry points, I move the initialization into my test's main() entry point:
library pricing_plans_test;

import 'package:unittest/unittest.dart';
import 'dart:html';
import 'package:polymer/polymer.dart';

main() {
  // tests here....
With that, I am ready to test. The tests themselves are the easy part, thanks in part to already having tested the same thing in JavaScript-land. My first two tests look like:
  group("[defaults]", (){
    test('name is "Plan"', (){

    test("can embed code", (){
      var content = query('pricing-plan').shadowRoot.query('content'),
          el = content.getDistributedNodes()[0];

        contains('plan content')
And both pass just fine. Yay!

As an avowed Dart homer, this probably will not come as much of a surprise, but this feels more stable than the JavaScript equivalent. It took me a while to sort out the HTML context and to realize that the normal Polyer.dart initialization was calling initPolymer(). But once I had those, writing the tests was easy. More importantly, write two tests was easy. Thanks to Dart's single point of entry, I didn't have to add any infrastructure in my test to ensure that Polymer was loaded or that it was not loaded twice. I needed only to reproduce the same application initialization in my test and I was good to go.

About the only complaint that I have is that getDistributedNodes() thing. C'mon Dart! I expected that to be just distributedNodes, not that JavaScript getter nonsense! I kid, I kid. Distributed nodes—where the stuff embedded in a template's <content> tag is rendered (i.e. distributed)—are a very new thing. I have no doubt that Dart will clean that up shortly, but if that is my biggest hardship, then I am in good shape testing-wise!

Day #945

No comments:

Post a Comment