Saturday, November 30, 2013

Wrap or Extend: What is the Best Polymer Practice?


I am really digging the possibilities opened up by UI-less Polymer elements. So far I found them super easy ways to establish event sinks or transforms on the documents in which they are inserted. They also make for a quick means of normalizing DOM events for tags that lack a nice interface. The possibilities are so wide open that it is hard to get a good sense of what makes for a good practice. But, since all of this is research material for a book named Patterns in Polymer, I need to develop that sense of best practice quickly.

Tonight, I am going to create an element that wraps last night's element. Whereas last night's <change-sink> wrapped contenteditable and <input> tags in a sane change event wrapper:
<change-sink>
  <div contenteditable><!-- Initial content here... --></div>
</change-sink>
<script>
  document.querySelector('change-sink').addEventListener('change', function(e){
    console.log('Was:');
    console.log(e.detail.was);
    console.log('But now it\'s different!');
  });
</script>
Tonight's should wrap the normalized change events from <change-sink> and save them for later use. The question that I would like to answer is, should I wrap elements like <change-sink> or is it better practice to combine change normalization and storage into a single element? I do not think that I will find a good answer tonight. Best practices are rarely found in a single day. Instead, I only hope to put in the necessary groundwork to answer that question down the road.

I start by importing the still-to-be-created store-changes.html into my web page and wrapping the <change-sink> element inside of the Polymer that will be defined:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/change-sink.html">
    <link rel="import" href="scripts/store-changes.html">
  </head>
  <body>
    <store-changes>
      <change-sink>
        <div contenteditable><!-- Initial content here... --></div>
      </change-sink>
    <store-changes>
  </body>
</html>
Next up, I create the imported change-sink.html. Again this is a UI-less Polymer, so I can jump right into defining the element's methods:
<polymer-element name="store-changes">
  <script>
    Polymer('store-changes', {
      ready: function() {
        this.addEventListener('change', this.storeChange);
      },
      storeChange: function(e) {
        var change = e.detail;
        console.log('Will store: ' + change.is);
      }
    });
  </script>
</polymer-element>
This tracer bullet has hit the mark—when I make a change, my new <store-changes> Polymer sees the normalized change from <change-sink> and is ready to store it:



From there, it is just a matter of mucking around with localStorage, first ensuring that the necessary data structure is in place:
    Polymer('store-changes', {
      // ...
      storeChange: function(e) {
        this.ensureStorage();
        // ...
      },
      ensureStorage: function() {
        if (localStorage.store_change !== undefined) return;
        localStorage.store_change = JSON.stringify({
          current: undefined,
          previous: []
        });
      }
    });
Of note here is that Polymer's addEventListener() appears to bind this to the current instance of the Polymer. That is, if I was to reference the current Polymer object, I can use this without having to explicitly bind it inside the addEventListener().

Next, I save a list of recent changes with each change event:

    Polymer('store-changes', {
      // ...
      storeChange: function(e) {
        this.ensureStorage();

        var change = e.detail,
            store = JSON.parse(localStorage.store_change);

        store.current = change;
        store.previous.unshift(change);
        store.previous.splice(10);

        localStorage.store_change = JSON.stringify(store);
      },
      // ...
    });
With that, I have myself a simple change history, local storage mechanism all triggered from simple change events. If I add a getter to the Polymer class:
    Polymer('store-changes', {
      // ...
      get: function(i) {
        return JSON.parse(localStorage.store_change).previous[i];
      },
      // ...
    });
Then I have a quick and simple means of retrieving that history from outside of the element:
el = document.querySelector('store-changes')
el.get(0)
// "<h1>Change #5</h1>"
el.get(1)
// "<h1>Change #4</h1>"
el.get(3)
// "<h1>Change #2</h1>"
That certainly looks like something that I could wrap in yet another Polymer to yield rudimentary undo/redo (maybe even with an actual UI!).

So far, I have to say that I like the separate elements like this. It feels very much like a convenient way of realizing separation of concerns in my code. That said, this could easily lead to deeply nested code and weird contextual behaviour depending on whether or not elements are parent, sibling, or child of other elements. And, since there are other ways to separate concerns, this seems a topic worth further exploration tomorrow.


Day #951


Friday, November 29, 2013

UI-less Polymer Element for Sane Change Events


Yesterday, I managed to scrape together a Polymer (JS) element that listened for events on the containing document. This was a non-UI element that was just in place to interact with other elements on the page. I think there is a lot of potential for this kind of thing, which is only one of the reasons that Polymer excites me enough to be writing a book on it.

Today, I hope to try the opposite. Instead of listening for events on the web page in which my custom Polymer element is contained, I would like to listen for events on an element inside my custom Polymer element. The idea in this case is to take something that is very hard to listen for events on, say a contenteditable <div>, and normalize them for easier consumption.

Something like this:
    <change-sink>
      <div contenteditable>
        <!-- initial content here... -->
      </div>
    </change-sink>
    <script>
      document.querySelector('change-sink').addEventListener('change', function(e){
        console.log('Was:');
        console.log(e.detail.was);
        console.log('But now it\'s different!');
      });
    </script>
Content editable stuff is a pain to identify when changes occur. It's possible, but far from easy. That's where this new <change-sink> tag comes in.

I start by importing the Polymer definition as usual:
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/change-sink.html">
  </head>
  <body>
    <change-sink>
      <div contenteditable>
        <!-- initial content here... -->
      </div>
    </change-sink>
  </body>
<script>
  <!-- change-sink listeners here... -->
</script>
</html>
I begin the definition of change-sink.html with a simple entered-view callback that grabs a reference to the element contained within <change-sink> and placing a keyup-console-logger on it:
<polymer-element name="change-sink">
  <script>
    Polymer('change-sink', {
      enteredView: function() {
        var that = this,
            el = this.children[0];
        el.addEventListener('keyup', function(e){
          console.log(el.innerHTML);
        });
      }
    });
  </script>
</polymer-element>
That does the trick as I now see the contenteditable contents change with each keyup:
<h1>Change e!</h1>
<h1>Change Me!</h1>
Instead of simply generating console messages, I want to support the API described earlier. When a change occurs, I should fire a "change" event whose details include the previous values for comparison. That is easy enough:
    Polymer('change-sink', {
      was: undefined,
      enteredView: function() {
        var that = this,
            el = this.children[0];
        el.addEventListener('keyup', function(e){
          that.fire('change', {was: that.was});
          that.was = el.innerHTML;
        });
      }
    });
I support a was property on the Polymer element itself. The previous value is then assigned there whenever a change occurs. Just before that assignment, I fire the change event with the previous value of the contained element.

And that works just fine! When I make my first change, the was value is undefined and, after the second change, the was value is defined properly:
Was:
undefined
But now it's different! 

Was:
<h1>Change Me!</h1>
But now it's different!
OK. That is pretty cool. Cool, but it is not actually firing on changes—just keyup. I'll get change events when I arrow key around my content editable <div>. I'll also get events with every character typed, not with every change. I am not going to see change events when content is pasted. Also, it would be cool if I could "change sink" <input> changes as well as content editable <div> tag changes.

All of that turns out to be pretty easy. I need to listen for a few more event types, use a debounce, and check for content in "value" if it is not in innerHTML:
    Polymer('change-sink', {
      was: undefined,
      child: undefined,
      enteredView: function() {
        this.child = this.children[0];

        var that = this;
        this.child.addEventListener('keyup', function(_){lazyChange(that)});
        this.child.addEventListener('blur',  function(_){lazyChange(that)});
        this.child.addEventListener('paste', function(_){lazyChange(that)});
        this.child.addEventListener('input', function(_){lazyChange(that)});
      }
    });

  var lazyChange = debounce(change, 750);

  function change(_el) {
    var el = _el.child;
    if (el.innerHTML == _el.was) return;

    _el.fire('change', {was: _el.was});
    _el.was = el.innerHTML || el.value;
  }
That does the trick. It now works with <textarea> just as easily as it does with content editable <div> tags. I can paste and see a change event. I can type new content, but only see one event. It is all pretty slick.



The actual heavy lifting comes from Polymer establishing the custom element and its relationship with the child element. Once that is in place, normalizing an otherwise ugly event proves very straight forward. Best of all, this is built for re-use. Anyone with this <change-sink> code now has normalized change events. I am eager to see what else I can do with this stuff!


Day #950

Thursday, November 28, 2013

UI-less Polymer Elements


When I think of web components, I normally think of creating new UI elements. But I think one of the more intriguing things that is possible with them is the ability to create UI-less features that simply serve as a convenient way to get access to streams of information. Instead of creating JavaScript objects that receive AJAX responses, create a Polymer element that does and then bubbles events in the document like an HTML element.

To explore the idea, I am going to try to create a JavaScript Polymer element that listens for click events on a web page. I start with the usual <script> setup and a custom <click-sink> element in the body:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/click-sink.html">
  </head>
  <body>
    <click-sink></click-sink>
  </body>
</html>
The backing element can then be nothing but a Polymer script that listens for events on the main document:
<polymer-element name="click-sink">
  <script>
    Polymer('click-sink', {
      enteredView: function() {
        this.ownerDocument.addEventListener('click', function(e){
          console.log('click');
        });
      }
    });
  </script>
</polymer-element>
And that works. When I click the page, I see "click" logged to the JavaScript console. It is of little use unless I transform that in some way. For tonight, I am content to transform that into a message that contains the X-Y coordinate at which the element was clicked. So I change the enteredView() method to:
      enteredView: function() {
        var that = this;
        this.ownerDocument.addEventListener('click', function(e){
          var xy = '' + e.clientX + ',' + e.clientY;
          that.fire('clicked', {msg: 'I got clicked at: ' + xy});
        });
      }
With that, I should be able to listen in my main document for the custom "clicked" event:
<!DOCTYPE html>
<html lang="en">
  <head><!-- load polymer stuff --></head>
  <body>
    <click-sink></click-sink>
  </body>
<script>
  document.addEventListener('clicked', function(e){
    console.log(e.detail.msg);
  });
</script>
</html>
Now, when I click on the page I get:
I got clicked at: 286,273
I got clicked at: 455,266
I got clicked at: 223,274 
Nice.

I am especially interested in extending this idea to web services and sockets. If the trouble of setting those up can be hidden behind a web component, leaving only streams of events, I think there can be some really fun possibilities. Which I will start to explore tomorrow.

Day #949

Wednesday, November 27, 2013

Running Polymer.dart Tests with Grunt Instead of Karma


I am a little bummed that I was unable to get Polymer.dart tests working under Karma. It's not a huge deal because I do not need Karma's multi-browser runner—this is Dart code, so write once, run everywhere. Even though I am just testing my Dart code and relying on Dart itself to support multiple browsers, it still would have been nice.

For posterity, I believe that the main problem was dynamically adding the <link> imports of my custom Polymer elements:
importPolymerElements(list) {
  list.forEach((definition) {
    var link = new LinkElement()
      ..rel = "import"
      ..href = "packages/pricing_panels/${definition}";
    query('head').append(link);
  });
}

main() {
  importPolymerElements(['pricing-plans.html', 'pricing-plan.html']);
  // Tests here...
}
Even if I disable Polymer, I still get Dartium errors:
URL must be imported by main Dart script: pricing_plans.dart 
URL must be imported by main Dart script: pricing_plan.dart
Even if I try dynamically creating these in my test without Karma, I still see the same.

Since this is already being run inside an active Dart isolate, the import of those Polymer elements, with <script> tags of their own, is uncool. When those <link> tags exist before Dart spins up, they can be imported along with the backing Polymer classes (and no main() entry point) without conflicting with an existing VM isolate.

There are probably ways around this, but they involve too much reaching under the covers of Polymer.dart and/or Karma for my tastes at this point. All I want is to rerun my tests whenever I make a code change. Grunt ought to be able to do that.

I already have Grunt installed (npm install -g grunt with a recent Node.js), so my next step is to install the grunt-contrib-watch module (to watch for test and code changes) and the grunt-shell module (to run tests in response to those changes). I start with this package.json:
{
  "name": "bootstrap-panels",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-shell": "~0.6.1",
    "grunt-contrib-watch": "~0.5.3"
  }
}
After npm install (with no arguments, npm installs whatever is listed in package.json), I am ready to teach Grunt which files to watch. In Gruntfile.js, I start with:
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    watch: {
      tests: {
        files: [
          'test/**/*.dart',
          'test/**/*.html',
          'lib/**/*.dart',
          'lib/**/*.html'
        ],
        tasks: ['shell:test']
      }
    }
  });
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-shell');
  grunt.registerTask('default', ['shell:test']);
};
Most of that is pretty basic Grunt and grunt-contrib-watch configuration. At the bottom, I load my tasks (watch and shell) and register a new shell command that I will use to run my actual tests. Above, I watch for changes to Dart and HTML code in my test and lib directories. If there are any changes, then I invoke the shell:test task.

All that remains is to add my shell:test task:
module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    watch: {
      tests: {
        files: [
          'test/**/*.dart',
          'test/**/*.html',
          'lib/**/*.dart',
          'lib/**/*.html'
        ],
        tasks: ['shell:test']
      }
    },
    shell: {
      test: {
        command: 'content_shell --dump-render-tree test/index.html',
        options: {
          stdout: true
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-shell');
  grunt.registerTask('default', ['shell:test']);
};
That is mercifully simple. When it is invoked, I run content_shell against my test page and display the output to STDOUT. I start the watcher with grunt watch, make a code change and:
➜  dart git:(master) ✗ grunt watch
Running "watch" task
Waiting...OK
>> File "test/packages/pricing_panels/pricing-plan.html" changed.

Running "shell:test" (shell) task
CONSOLE MESSAGE: line 221: unittest-suite-wait-for-done
CONSOLE MESSAGE: line 187: PASS: [defaults] name is "Plan"
CONSOLE MESSAGE: line 187: PASS: [defaults] can embed code
CONSOLE MESSAGE: line 191: 
CONSOLE MESSAGE: line 197: All 2 tests passed.
CONSOLE MESSAGE: line 221: unittest-suite-success
Content-Type: text/plain
layer at (0,0) size 800x600
  RenderView at (0,0) size 800x600
layer at (0,0) size 800x600
  RenderBlock {HTML} at (0,0) size 800x600
layer at (8,8) size 784x584
  RenderBody {BODY} at (8,8) size 784x584
#EOF
#EOF

Done, without errors.
Completed in 1.374s at Wed Nov 27 2013 23:52:01 GMT-0500 (EST) - Waiting...
It works! Yay!

I will probably take some time to add exit code handling, but that seems like it will work quite nicely. I may not have Karma's ability to run the tests in multiple browsers, but with Dart, that is hardly a loss.


Day #948

Tuesday, November 26, 2013

No Joy with Karma with Polymer.dart


While writing the testing chapter in Patterns in Polymer, I found myself putting the Karma test runner section entirely inside a ifdef:js block of text. This got me to thinking. I really like Karma. I really like Dart. Can the two of them get along? Can Karma go in both the Dart and JavaScript versions of the book?

Well, there is one way to find out...

I already have a couple of tests written in Dart's unittest. There is a karma-dart testing framework definition. Maybe that'll just work. I start by installing the plugin via node.js's npm:
$ npm install karma-dart --save-dev
I start with the same karma.conf.js that I have been using for my JavaScript Polymer, but I change the framework (to dart-unittest) and the list of files to be used:
    // ...
    // frameworks to use
    frameworks: ['dart-unittest'],

    // list of files / patterns to load in the browser
    files: [
      'test/*.dart',
      {pattern: '**/*.dart', watched: true, included: false, served: true}
      // 'packages/browser/dart.js',
      // 'packages/browser/interop.js'
    ],
    // ...
I am not sure about those two browser package dependencies, so I leave them commented out at first. But when I run Karma and attach Dartium to http://localhost:9876, I find that I need those after all:
...
Error: ENOENT, no such file or directory '/home/chris/repos/polymer-book/play/bootstrap-panels/dart/packages/js/js.dart'
    at Object.fs.openSync (fs.js:427:18)
    at Object.fs.readFileSync (fs.js:284:15)
...
Since I do not need these in my Polymer code, I add them as additional development dependencies in my Dart Pub pubspec.yaml:
name: pricing_panels
dependencies:
  polymer: any
dev_dependencies:
  unittest: any
  browser: any
  js: any
After a pub get, I restart my Karma server and try again in Dartium.

I no longer have errors about the js package. Now both of my tests are run and both fail with similar error messages:
Test failed: Caught The null object does not have a getter 'text'.

NoSuchMethodError : method not found: 'text'
Receiver: null
Arguments: [] 
This is failing because nothing is importing my Polymer.dart element definitions. Without them, my custom <pricing-plan> elements have no shadow DOM, causing tests that probe the shadow DOM to fail:
      expect(
        query('pricing-plan').shadowRoot.text,
        contains('Plan')
      );
In my non-Karma test, I simply imported these elements in the test context page:
<head>
  <!-- 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>
</head>
That will not work here, so I have manually add them in my test:
importPolymerElements(list) {
  list.forEach((definition) {
    var link = new LinkElement()
      ..rel = "import"
      ..href = "/packages/pricing_panels/${definition}";
    query('head').append(link);
  });
}

main() {
  importPolymerElements(['pricing-plans.html', 'pricing-plan.html']);
  // tests here..
}
Unfortunately, that does not work. I am still left with a distinct lack of shadow DOM on my custom Polymer elements. Furthermore, I am seeing Dartium errors like:
URL must be imported by main Dart script: pricing_plans.dart 
URL must be imported by main Dart script: pricing_plan.dart 
I am unsure what I might be doing that would cause Karma / unittest / Dartium to attempt to load these script files outside the context of my existing main() entry point that is running my test and Polymer.

Stumped, I call it a night. I may or may not revisit this. It is probably sufficient to Grunt a content_shell test run, which I may try tomorrow (hopefully with more luck).


Day #947

Monday, November 25, 2013

Dynamically Generating Polymer.dart Elements (revisited)


I have a little work ahead of me as I figure out how to run large amounts of acceptance tests against the code in Patterns in Polymer. The test suites that exist around Polymer and Polymer.dart are exquisite, but tend to be of the unit test variety. That's great for API tests, but I need to write tests from the perspective of a human reader, not a program context. Hence the need to nail down acceptance testing.

I think I have a better handle on this in Dart than I do in JavaScript. Although testing Polymer.dart has proven to be different than acceptance testing in projects and in Dart for Hipsters, there are enough similarities that I feel like I ought to be OK. But before switching back, I would like to push past the first two tests that I wrote last night. That means that I need to be able to add and remove Polymer elements per-test (or test group).

When I was first exploring Polymer.dart a while back, I found that I could dynamically create Polymer elements with createElement(). That method appears to have since been removed (it was marked as temporary at the time). Without it, I might try to create my <pricing-plan> element in a setup block like so:
  var _el;
  setUp((){
    _el = new Element.html('<pricing-plan>plan content</pricing-plan>');
    document.body.append(_el);
  });
That does not make it past Dart's built-in HTML sanitizer:
Removing disallowed element <PRICING-PLAN>
Removing disallowed element <PRICING-PLAN>
ERROR: [defaults] name is "Plan"
  Setup failed: Caught Bad state: No elements
The aforementioned createElement() was good for getting around this problem with registered, custom elements. In the meantime, the ability to dynamically create these registered, custom elements has moved into dart:html in the form of Element.tag():
  var _el;
  setUp((){
    _el = new Element.tag('pricing-plan')
      ..innerHtml = 'plan content';
    document.body.append(_el);
  });
As far as I can tell, there is no easy way to insert raw custom tag HTML, which itself might contain other custom Polymer elements. At least not without falling back to “null tree sanitizers” to skip Dart's HTML sanitization:
class NullTreeSanitizer implements NodeTreeSanitizer {
  void sanitizeTree(node) {}
}

main() {
  initPolymer();

  var _el;
  setUp((){
    _el = new Element.html(
      '<pricing-plan>plan content</pricing-plan>',
      treeSanitizer: new NullTreeSanitizer()
    );
    document.body.append(_el);
  });
  // Tests here...
}
While that works, I do not relish throwing around that treeSanitizer parameter in all of my setup. So instead I recreate the createElement() method of old, but only for use in my tests:
class NullTreeSanitizer implements NodeTreeSanitizer {
  void sanitizeTree(node) {}
}

createElement(String html) =>
  new Element.html(html, treeSanitizer: new NullTreeSanitizer());

main() {
  initPolymer();

  var _el;
  setUp((){
    _el = createElement('<pricing-plan>plan content</pricing-plan>');
    document.body.append(_el);
  });
  // Tests here...
}
I think I can live with that solution, at least for my tests. I suppose that, if I found myself dynamically generating large numbers of custom-element-containing HTML snippets from strings in actual application code that I could do something similar to this createElement(). Then again, that seems the very purpose of Polymer, so this may very well be a testing use-case only. Regardless, I think I have this sorted out well enough. Time for some writing...


Day #946

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
asset
└── bootstrap.min.css
lib
├── 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:
<head>
  <!-- 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>
</head>

<body>
  <pricing-plan>plan content</pricing-plan>
</body>
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() {
  initPolymer();
  // 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"', (){
      expect(
        query('pricing-plan').shadowRoot.text,
        contains('Plan')
      );
    });

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

      expect(
        el.text,
        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

Saturday, November 23, 2013

Converting JavaScript Polymer to Dart


I still have more questions than I'd like in my JavaScript implementation of a Polymer + Boostrap element. But I think most of those questions can be deferred until later. Tonight I convert my custom Polymer elements into Dart using Polymer.dart.

I am unsure what the code organization ought to be, so I am going to guess tonight and circle back around later to see if any changes are needed. Loosely following Dart Pub guidelines, I create an asset directory to hold my Polymer.dart HTML templates and my bootstrap CSS, a lib directory for my Polymer.dart code, and a web directory for my sample application web page.

Next, I create the usual pubspec.yaml listing only Polymer as a library (non-development) dependency:
name: pricing_panels
dependencies:
  polymer: any
dev_dependencies:
  unittest: any
After a quick pub fetch, I am ready to go. Starting with index.html, I have very similar HTML to the JavaScript version:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">

    <link type="text/css" rel="stylesheet" href="/assets/pricing_panels/bootstrap.min.css">

    <!-- Load component(s) -->
    <link rel="import" href="/assets/pricing_panels/pricing-plans.html">
    <link rel="import" href="/assets/pricing_panels/pricing-plan.html">

    <!-- Load Polymer -->
    <script type="application/dart">export 'package:polymer/init.dart';</script>
    <script src="packages/browser/dart.js"></script>
  </head>
  <body>
    <!-- pricing plan html here -->
  </body>
</html>
The stuff that I placed in the asset directory is, by Pub convention, accessible from the /assets/<package name>/ URL space, which is where the Polymer element definitions and Boostrap CSS are coming from. Also in there is the normal Polymer.dart initialization code. Well, the new normal at least as this has changed slightly since the last time I played with this project.

Taking a closer look at the Polymer element definition, not much needs to change from the JavaScript version:
<polymer-element name="pricing-plan" attributes="name type size">
  <template>
    <div class="col-md-{{size}}">
      <div class="panel panel-{{type}}">
        <div class="panel-heading">
          <h3 class="panel-title">{{name}}</h3>
        </div>
        <div class="panel-body">
          <content></content>
        </div>
      </div>
    </div>
  </template>
  <script type="application/dart" src="/packages/pricing_panels/pricing_plan.dart"></script>
</polymer-element>
In fact, the only thing that has changed is the <script> tag, which, naturally enough, now points to Dart code. Since the Dart code resides in the lib directory, the URL is slightly different than the asset HTML URL.

The Dart code itself looks like:
import 'package:polymer/polymer.dart';
import 'dart:html';

@CustomTag('pricing-plan')
class PricingPlanElement extends PolymerElement {
  @observable String name = 'Plan';
  @observable String type = 'default';
  @observable int size = 4;

  PricingPlanElement.created() : super.created();
}
That is more verbose than the JavaScript version, but only slightly, due to types and code annotations. The equivalent JavaScript was:
    Polymer('pricing-plan', {
      name: 'Plan',
      type: 'default',
      size: 1
    });
Once I have similar definitions for the other custom element in this package (the <pricing-plans> container), this actually works. Almost.

Interestingly, the CSS styles from the main document are not being applied:



To get the Boostrap CSS into the shadow DOM elements, I have to tell the Dart to allow "author" (from the main page) styles:
import 'package:polymer/polymer.dart';
import 'dart:html';

@CustomTag('pricing-plan')
class PricingPlanElement extends PolymerElement {
  @observable String name = 'Plan';
  @observable String type = 'default';
  @observable int size = 4;

  PricingPlanElement.created() : super.created() {
    shadowRoot.applyAuthorStyles = true;
  }
}
With that, I have my Bootstrap panels:



What is interesting is that I did not need to tell my JavaScript version to do the same thing. I am unsure if this is due to different Chrome versions (31 for Dart, 32 for JavaScript) or the embedded Dart VM in the former. The “author styles” thing seems to be a real thing, so I would expect that it is needed in the JavaScript version as well. Ah well, grist for another day.

For now, I have made the successful transition with a minimum of pain. Up tomorrow, I will explore testing of Polymer in Dart. Hopefully thanks to Dart's excellent testing and my hard-earned knowledge of Polymer testing, this will go a little smoother than when I was earning that knowledge.


Day #944

Friday, November 22, 2013

Jasmine Setup of Polymer Tests


Testing my fairly simple Polymer elements has been an unexpected adventure. But I think that I may have cracked it.

Although last night's excursion into testing the test Shadow DOM will no doubt be of use at some point, it feels more like testing the framework than serving to test something of real value. I think that the tests that I wrote previously capture that spirit. And, as I found when I tried my second test, I suspect that adding a second specification to the same test suite will teach me.

I continue to test a couple of Boostrap web components: <pricing-plans> and its child <pricing-plan. The idea is to convert the mess of <div> tags that usually build something like:



With Polymer web components, these can be expressed as the much more readable:
<pricing-plans>
  <pricing-plan name="Multi-Language">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="I Only Like One">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="Extras" type="primary">
    <ul>
      <li>Get the multi-language pack plus…</li>
      <li><strong>Screencast</strong> of <span title="Test Driven
        Development">TDDing</span> a polymer element.</li>
      <li>The <strong>warm feeling</strong> of helping
        produce the book.</li>
      <li>Inclusion in the <strong>supporters</strong> section of the
        book.</li>
      <li>Chris's undying <strong>love</strong>.</li>
      <li>More...?</li>
    </ul>
  </pricing-plan>
</pricing-plans>
I might quibble over the difficulty in distinguishing between the parent and child tag names, but that aside, this is more more readable than slogging through a bunch of <div class="col-md-10 col-sm-4"> tags in order to find my actual content.

What I have come to understand as I have explored testing the child <pricing-plan> tags is that the hardest thing to get right is the way that Polymer wants to be loaded—first:
  <head>
    <!-- ... -->
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/pricing-plans.html">
    <link rel="import" href="scripts/pricing-plan.html">
  </head>
In the spec for the <pricing-plan> tag, I had been doing that with a “before all” test setup block. Before all specs, I dynamically inserted the Polymer library in the test page's <head> so that it would be loaded before anything else. And that worked.

But it raised the question: what happens if there is a second specification file with its own “before all”? Should the second “before all” detect the presence of the Polymer library from the first? Should both detect Polymer so that their order can be changed? Bother that.

Instead, I think I can lean on my testing framework (Jasmine) and my test runner (Karma) to fix this. I start with the later, where I make the first included file a new PolymerSetup.js:
    // ...
    /**
     * Don't include Polymer HTML and JS because Polymer is very
     * particular about the order in which they are added. Serve them,
     * but defer loading to the test setup. Include test HTML
     * fixtures.
     */
    // list of files / patterns to load in the browser
    files: [
      'test/PolymerSetup.js',

      {pattern: 'scripts/**/*.html', included: false},
      {pattern: 'scripts/**/*.js', included: false},
      {pattern: 'scripts/**/*.js.map', included: false},
      'test/**/*Spec.js',
      'test/*.html'
    ],
    // ...
In that setup, I load Polymer and the Polymer elements that I am trying to test:
// 1. Load Polymer before any code that touches the DOM.
var script = document.createElement("script");
script.src = "/base/scripts/polymer.min.js";
document.getElementsByTagName("head")[0].appendChild(script);

// 2. Load component(s)
var link = document.createElement("link");
link.rel = "import";
link.href = "/base/scripts/pricing-plan.html";
document.getElementsByTagName("head")[0].appendChild(link);

link = document.createElement("link");
link.rel = "import";
link.href = "/base/scripts/pricing-plans.html";
document.getElementsByTagName("head")[0].appendChild(link);
What I like about this approach is that it mimics the HTML inclusion order in the normal application code. First polymer is loaded, then the Polymer elements.

This then allows the Jasmine specs to just worry about the fixtures that directly affect its tests:
describe('<pricing-plan>', function(){

  var container;

  beforeEach(function(){
    container = document.createElement("div");
    container.innerHTML = __html__['test/pricing-plan-fixture.html'];
    document.body.appendChild(container);
    waits(0); // One event loop for elements to register in Polymer
  });
  afterEach(function(){
    document.body.removeChild(container);
  });
  // Tests here...
});
Best of all, this appears to work for both set of test files.

UPDATE In order to ensure that Polymer was ready, I found that I needed a Jasmine beforeEach() back in the PolymerSetup.js that waits for CustomElements to be ready (the same as waiting for the WebComponentsReady event to fire):
// Delay Jasmine specs until WebComponentsReady
beforeEach(function(){
  waitsFor(function(){
    if (typeof(CustomElements) == 'undefined') return false;
    return CustomElements.ready;
  });
});


Day #943

Thursday, November 21, 2013

Testing Content Wrapped by Polymer Elements


After last night, I have a setup for testing Polymer elements. I don't know if it is a good setup, let along the setup. Luckily, I have a reasonably involved pair of Polymer elements to try things out with.

As I found yesterday, because of the way that Polymer likes to establish itself and ready itself to do its web component thing, the setup in Jasmine tests was a little awkward. In application code, the <head> of the HTML document needs to look like:
  <head>
    <!-- ... -->
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/pricing-plans.html">
    <link rel="import" href="scripts/pricing-plan.html">
  </head>
The best that I could come up with to simulate this in my Jasmine setup was:
describe('<pricing-plan>', function(){
  var previouslyLoaded = false;

  beforeEach(function(){
    if (!previouslyLoaded) {
      var script = document.createElement("script");
      script.src = "/base/scripts/polymer.min.js";
      document.getElementsByTagName("head")[0].appendChild(script);

      var link = document.createElement("link");
      link.rel = "import";
      link.href = "/base/scripts/pricing-plan.html";
      document.getElementsByTagName("head")[0].appendChild(link);

      document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
    }

    previouslyLoaded = true;
    waits(100);
  });
  // Tests go here...
});
There is not really a “before all” built into Jasmine which is why I added in the ugly previouslyLoaded conditional.

Anyhow, what I would like to test tonight is that the content that I wrap with my Polymer element winds up being rendered in the proper location. This turns out to be much harder than I expected. The fixture that I am placing into my document.body is pretty simple:
<pricing-plan>pricing plan 01</pricing-plan>
I just need to verify that the string "pricing plan 01" is embedded inside of the Polymer element. If I inspect the element on the debug page, it is definitely there:



So how hard can it be to query programatically? Well, it involves something called distributed nodes. It seems that the text content from my fixture is still in the original element. If I query the element's text content from the JavaScript, I see it:
> document.querySelector('pricing-plan').textContent
  "pricing plan 01"
I see that text, but that is not where the content is "distributed" to be rendered.

In fact, it is "distributed" for rendering in the <content> tag of my Polymer element:
<polymer-element name="pricing-plan" attributes="name type size">
  <template>
    <div class="col-md-{{size}}">
      <div class="panel panel-{{type}}">
        <div class="panel-heading">
          <h3 class="panel-title">{{name}}</h3>
        </div>
        <div class="panel-body">
          <content></content>
        </div>
      </div>
    </div>
  </template>
  <script>
    Polymer('pricing-plan', { /* ... */ });
  </script>
</polymer-element>
And to test for that, I need to go through my Polymer element's shadow DOM. The >content> element resides there in the "shadows" of the real DOM element, my custom <pricing-plan> element. Once I have that, use the getDistributedNode() method to get the first node that was distributed. Hopefully that is my text snippet:
    it('contains the content wrapped by the element', function() {
      var distributed_nodes = el.shadowRoot.
           querySelector('content').
           getDistributedNodes();

      expect(distributed_nodes[0].textContent).toBe('pricing plan 01');
    });
And that actually works.

It may work, but that's pretty awkward. Testing this shadow DOM stuff is much trickier than I expected. But tested it must be. I wonder if that is the last of the surprises. Almost certainly not.


Day #942

Wednesday, November 20, 2013

Actual Testing of Polymer Elements


Last night, I was able to get the Karma test runner to test a Polymer element. I think.

Honestly, I have good reason to think that it worked: the debug page in Chrome included the Shadow DOM. But I did not actually test any of the Polymer code or resulting display, so tonight I make sure that I have this working.

By way of review, I setup Karma with mostly default values (including using the Jasmine testing framework). The only changes were to preprocess and include test/*.html testing templates and to serve but not include the Polymer files in the testing context page. The relevant section of the configuration:
    /**
     * Compile HTML into JS so that they can be used as templates
     */
    preprocessors: {
      'test/*.html': 'html2js'
    },

    /**
     * Don't include Polymer HTML and JS because Polymer is very
     * particular about the order in which they are added. Serve them,
     * but defer loading to the test setup. Include test HTML
     * fixtures.
     */
    // list of files / patterns to load in the browser
    files: [
      {pattern: 'scripts/**/*.html', included: false},
      {pattern: 'scripts/**/*.js', included: false},
      'test/**/*Spec.js',
      'test/*.html'
    ],
The Polymer project is picky about when Polymer.js needs to be loaded. Well, not too picky—just before code that touches the DOM:
  <head>
    <!-- ... -->
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/pricing-plans.html">
    <link rel="import" href="scripts/pricing-plan.html">
  </head>
This is why I could not allow Karma to include the library itself—by the time Karma includes scripts, it is too late. So my Jasmine test setup does that:
describe('<pricing-plan>', function(){
  beforeEach(function(){
    var script = document.createElement("script");
    script.src = "/base/scripts/polymer.min.js";
    document.getElementsByTagName("head")[0].appendChild(script);

    var link = document.createElement("link");
    link.rel = "import";
    link.href = "/base/pricing-plan.html";
    document.getElementsByTagName("head")[0].appendChild(link);

    document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
  });
  // Tests go here...
});
And, as I mentioned at the outset, that seems to work. Karma's debug test page has the results that I would expect. But I am only testing that the <pricing-plan> tag, which is defined in the testing template, exists:
  // ...
  describe('defaults', function(){
    var el;
    beforeEach(function(){
      el = document.getElementsByTagName('pricing-plan')[0];
    });

    it('should create an element', function() {
      expect(el).toNotBe(undefined);
    });
  });
  // ...
That test would pass with or without Polymer running properly (and it did last night, which caused me more than a little confusion). What I really want to know about this particular polymer element is: does its shadow DOM include all of the Bootstrap <div> tags that I am trying to hide?

And that's where everything falls apart. Of course, it's always the second test that breaks things...

If I add a test that looks for the default pricing plan's ("Plan") in the shadow DOM of my custom element, it passes. But only if I comment out the original test:
  describe('defaults', function(){
    var el;
    beforeEach(function(){
      el = document.getElementsByTagName('pricing-plan')[0];
    });

    // it('should create an element', function() {
    //   expect(el).toNotBe(undefined);
    // });

    it('defaults to "Plan" as the name', function() {
      expect(el.shadowRoot.textContent).toContain('Plan');
    });
  });
If I have both tests running, then I get assertion failed messages from the second test:
FAILED <pricing-plan> defaults defaults to "Plan" as the name 
More bothersome is an endless stream of Polymer call stack errors:
RangeError {stack: "RangeError: Maximum call stack size exceeded↵    a…ripts/polymer.min.js:32:16781), <anonymous>:2:12)", message: "Maximum call stack size exceeded"}
 "RangeError: Maximum call stack size exceeded
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:1:10)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)" polymer.min.js:32
RangeError {stack: "RangeError: Maximum call stack size exceeded↵    a…ripts/polymer.min.js:32:16781), <anonymous>:2:12)", message: "Maximum call stack size exceeded"}
 "RangeError: Maximum call stack size exceeded
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:1:10)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)
    at Object.eval (eval at k (http://localhost:9876/base/scripts/polymer.min.js:32:16781), <anonymous>:2:12)" 
After a little investigation, I surmise that this is due to the beforeEach() setup which adds the Polymer script file multiple times. And this seems to be the case as I can get both tests passing with a conditional that loads the Polymer files only once:
describe('<pricing-plan>', function(){
  var previouslyLoaded = false;

  beforeEach(function(){
    if (!previouslyLoaded) {
      var el = document.createElement("script");
      el.src = "/base/scripts/polymer.min.js";
      document.getElementsByTagName("head")[0].appendChild(el);

      var link = document.createElement("link");
      link.rel = "import";
      link.href = "/base/scripts/pricing-plan.html";
      document.getElementsByTagName("head")[0].appendChild(link);

      document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
    }

    previouslyLoaded = true;
    waits(100);
  });
  //  Multiple tests here....
});
Two other things that I note here are that the document.body.innerHTML replacement also needs to be done only once. If that is brought outside the conditional, the second test will fail. Similarly, the waits(100) at the end of the setup also seems to be necessary to give Polymer adequate time to get ready to do its thing.

This does not feel terribly robust. What happens, for instance, I try to test multiple tags with different attributes or dynamically added custom elements? What if I need to test multiple Polymer elements that work together? Will that setup stand up or will I see the return of strange errors? Also, I feel as though I am likely missing a Polymer callback or two with the need for waits(100). Hopefully I can answer those questions and make some improvements in the days to come.


Day #941


Tuesday, November 19, 2013

Getting Started with Karma for Polymer Testing


The end of Patterns in Polymer will deal with testing and maintainability. I can't leave research on testing until the book is nearly done. In fact, it is rather important that I nail it down very early so that I have assurance that the code referenced throughout the book works - especially as the Polymer project continues to evolve.

I find it interesting that my posts on testing tend to have fewer readers. I suppose I understand in a way why this is. It is always fun to play and create with new tools. I like it too. But a tool that does not support strong testing practices is a tool that promotes codebases that will rot. So I quickly move from introduction to testing. If I am not satisfied, it is time to drop the new shininess for something better.

Testing is one of the reasons I love Dart so much. I have already written some extensive tests in Polymer.dart, so I am quite comfortable with that aspect of testing in Patterns in Polymer. But testing the JavaScript version? No idea.

The only mention of testing on the project page discusses building Polymer itself, not a project. Searches currently return very little, so it seems that I am on my own for now. In JavaScript land, I have been defaulting to Karma and Jasmine.

I already have Karma installed on my system:
➜  bootstrap-panels git:(master) karma --version
Karma version: 0.10.2
If I did not, installing it is a simple matter of installing Node.js and then npm install -g karma.

There is a lot to love about Karma, but getting started may at the top of the list. The built-in karma init is wonderful:
➜  bootstrap-panels git:(master) karma init

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture a browser automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/**/*Spec.js
> 

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

Config file generated at "/home/chris/repos/polymer-book/play/bootstrap-panels/karma.conf.js".
Normally I would also include PhantomJS in the list of browsers for better continuous integration testing support, but tonight I just want to smoke test my testing. Chrome should be sufficient for that.

I need two changes to the default values that I wrote to karma.conf.js. First, I need to include the Polymer JavaScript source into the Jasmine context.html page. This is done by adding 'scripts/**/*.js' to the list of files section the configuration. The other change that I make is to load fixture data. I am not a huge fan of fixture files for JavaScript testing, but I am even less a fan of string manipulation in JavaScript, so I use fixtures. For these, I need to include them in the list of files and add an html2js preprocessor:
module.exports = function(config) {
  config.set({
    // ...
    preprocessors: {
      'test/*.html': 'html2js'
    },

    // list of files / patterns to load in the browser
    files: [
      'scripts/**/*.js',
      'test/**/*Spec.js',
      'test/*.html'
    ],
    // ...
  });
};
For my tests tonight, I create pricing-plan-test.html fixture file with minimal content:
<pricing-plan>pricing plan 01</pricing-plan>
With that, I am ready to write my first test in test/PricingPlanSpec.js. I start by loading that fixture in a beforeEach() block:
describe('<pricing-plan>', function(){
  beforeEach(function(){
    document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
  });
  // ...
});
Since my current fixture is just testing default values (there are no attributes), I create a "defaults" testing group. Before each test in this group, I find my fixture element:
describe('<pricing-plan>', function(){
  beforeEach(function(){
    document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
  });

  describe('defaults', function(){
    beforeEach(function(){
      var it = document.getElementsByTagName('pricing-plan')[0];
    });
    // tests here...
  });
});
As for the test itself, I just start tonight by verifying that the element is there:
describe('<pricing-plan>', function(){
  beforeEach(function(){
    document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
  });

  describe('defaults', function(){
    beforeEach(function(){
      var it = document.getElementsByTagName('pricing-plan')[0];
    });

    it('should create an element', function() {
      expect(it).toNotBe(undefined);
    });
  });
});
With that, I can fire up my test to find:
➜  bootstrap-panels git:(master) ✗ karma start
INFO [karma]: Karma v0.10.2 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 33.0.1707 (Linux)]: Connected on socket MUJraHBb3vxX3N1lzlTY
Chrome 33.0.1707 (Linux): Executed 1 of 1 SUCCESS (0.309 secs / 0.018 secs)
That is not quite success, however. Polymer takes some time to load and my test is not waiting for that before running the tests. I am only verifying that the bare tag exists, not that the Polymer element is doing anything. I think that is going to be a more significant undertaking, possible involving requirejs to ensure script load order (Polymer really needs to come first). I will pick back up with that tomorrow.

UPDATE: I was able to get the Polymer code firing with some addition beforeEach() setup:
describe('<pricing-plan>', function(){
  beforeEach(function(){
    var script = document.createElement("script");
    script.src = "/base/scripts/polymer.min.js";
    document.getElementsByTagName("head")[0].appendChild(script);

    var link = document.createElement("link");
    link.rel = "import";
    link.href = "/base/pricing-plan.html";
    document.getElementsByTagName("head")[0].appendChild(link);

    document.body.innerHTML = __html__['test/pricing-plan-fixture.html'];
  });
  // tests here...
});
By manually appending Polymer and my polymer element to the script tag, I was able to get the actual Polymer element content to show up in the Karma test runner:



I had to instruct Karma to serve, but not automatically include, these files:
    // ...
    // list of files / patterns to load in the browser
    files: [
      {pattern: '*.html', included: false},
      {pattern: 'scripts/**/*.js', included: false},
      'test/**/*Spec.js',
      'test/*.html'
    ],
    // ...
I am not 100% sold that this is better than futzing with require.js, but it seems to work. I will play more with this tomorrow.


Day #940

Monday, November 18, 2013

Working with Multiple Polymer Elements


I will be the first to admit that I have no idea if it is a good idea to couple Bootstrap and Polymer like I have been doing over the past few days. It mostly just scratched an annoying itch that I got from putting together the Patters in Polymer landing page. The result has been quite pleasant and I think that I have the opportunity to take it a step further.

Instead of the usual crazy amount of Bootstrap <div>s, I have reduced my HTML to a very clean:
<div id="price">
  <div class="container">
    <div class="col-md-10 col-sm-4">
    <!-- pricing plans here... -->
      <pricing-plan name="Extras" type="primary">
        <ul>
          <li>Get the multi-language pack plus…</li>
          <li><strong>Screencast</strong> of <span title="Test Driven
            Development">TDDing</span> a polymer element.</li>
          <li>The <strong>warm feeling</strong> of helping
            produce the book.</li>
          <li>Inclusion in the <strong>supporters</strong> section of the
            book.</li>
          <li>Chris's undying <strong>love</strong>.</li>
          <li>More...?</li>
        </ul>
      </pricing-plan>
    </div> <!-- class="col-md-10 col-md-offset-1 col-sm-4 " -->
  </div> <!-- /.container -->
</div> <!-- /#price -->
Which, thanks to the Polymer definition of <pricing-plan> results in Bootstrap's nice panels:



Really, the only thing still cluttering up the pricing plan HTML is the container code. If I could reduce that to simply <pricing-plans>, life would be much simpler. It does not hurt that this would be my first attempt a two interacting Polymer elements.

Actually, this is easy, right? I need another Polymer element whose template is those Bootstrap <div>s wrapping a <content></content> declaration. In Polymer, <content> is like a Ruby yield - it yields whatever the container is wrapping. So I do the usual, starting with a new <link> tag in the main page:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="scripts/polymer.min.js"></script>
    <link rel="import" href="pricing-plans.html">
    <link rel="import" href="pricing-plan.html">
  </head>
  <body>
    <!-- HTML, including Polymer elements, here... -->
  </body>
</html>
The simple <content> wrapping pricing-plans.html is then:
<polymer-element name="pricing-plans">
  <template>
    <div id="price">
      <div class="container">
        <div class="col-md-10 col-sm-4">
          <content></content>
        </div>
      </div>
    </div>
  </template>
  <script>
    var ss = document.createElement("link");
    ss.type = "text/css";
    ss.rel = "stylesheet";
    ss.href = "css/bootstrap.min.css";
    document.getElementsByTagName("head")[0].appendChild(ss);

    Polymer('pricing-plans');
  </script>
</polymer-element>
The only thing making this anything other than stock Polymer is the dynamic inclusion of the Bootstrap CSS (which was part of the <pricing-plan> definition last night. With that, the HTML in my main document becomes:
<pricing-plans>
  <pricing-plan name="Multi-Language">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="I Only Like One">
    <ul><!-- ... --></ul>
  </pricing-plan>
  <pricing-plan name="Extras" type="primary">
    <ul>
      <li>Get the multi-language pack plus…</li>
      <li><strong>Screencast</strong> of <span title="Test Driven
        Development">TDDing</span> a polymer element.</li>
      <li>The <strong>warm feeling</strong> of helping
        produce the book.</li>
      <li>Inclusion in the <strong>supporters</strong> section of the
        book.</li>
      <li>Chris's undying <strong>love</strong>.</li>
      <li>More...?</li>
    </ul>
  </pricing-plan>
</pricing-plans>
Pretty!

The last thing that I would like to do it automatically resize the panels depending on how many of them there are. I do not know if it is possible for the parent element, <pricing-plans> to communicate to the child objects. I am not registering them in any way, so I would tend to think not. So instead, I teach the individual <pricing-plan> elements how to do this.

Back in the pricing-plan.html definition, I add a ready() callback method to the element's definition:
<polymer-element name="pricing-plan" attributes="name type size">
  <template><!-- ... --></template>
  <script>
    Polymer('pricing-plan', {
      name: 'Plan',
      type: 'default',
      size: 1,
      ready: function() {
        this.size = 12 / this.parentElement.childElementCount;
      }
    });
  </script>
</polymer-element>
And that actually works! If I remove one of the plans, leaving two, the remaining plans now occupy half of the normal Boostrap 12 columns.



Nice. It is really impressive how little code is required to realize some significant HTML clean-up. I am also enjoying how nice it is to work with the "easy" stuff. I still might like to investigate if actual communication is possible between elements, rather than just probing the environment. But, for now, I will happily take this little win.


Day #939

Sunday, November 17, 2013

Further Adventures with Polymer and Bootstrap Panels


I had a nice, calming time with Polymer (the JavaScript flavor) last night. Having fought for much of the day with Bootstrap, I was happy to convert a bunch of panels like so:
<div id="price">
  <div class="container">
    <div class="col-md-10 col-md-offset-1 col-sm-4 ">
      <div class="panel panel-default col-md-3">
        <div class="panel-heading">
          <h3 class="panel-title">Multi-Language</h3>
        </div>
        <div class="panel-body">
          <ul>
            <li>Get the <strong>JavaScript</strong> version!</li>
            <li>Get the <strong>Dart</strong> version!</li>
            <li>Private <strong>GitHub repository</strong> access to see how
            it's done.</li>
          </ul>
        </div>
      </div> <!-- /.panel1 -->
      <!-- More panels here -->
  </div> <!-- /.container -->
</div> <!-- /#price -->
Into a much less noisy version that looks like:
<div id="price">
  <div class="container">
    <div class="col-md-10 col-md-offset-1 col-sm-4">
      <pricing-plan name="Multi-Language">
        <ul>
          <li>Get the <strong>JavaScript</strong> version!</li>
          <li>Get the <strong>Dart</strong> version!</li>
          <li>Private <strong>GitHub repository</strong> access to see how
            it's done.</li>
        </ul>
      </pricing-plan>
      <!-- More pricing plans here -->
    </div> <!-- /.col-md-10 -->
  </div> <!-- /.container -->
</div> <!-- /#price -->
Aside from the containing col-md-10 (which I will worry about tomorrow), that is a very pleasant and, dare I say it, semantic bunch of content.

I left off last night wanting to be able to adjust the panel type and size. Both of those are fairly trivial modifications to the pricing-plan.html polymer-element definition that is referenced by my page:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <script src="scripts/polymer.min.js"></script>
    <link rel="import" href="pricing-plan.html">
  </head>
  <!-- html document here... -->
</html>
The fix is it add more published attributes (along with sane default values) to the element definition:
<polymer-element name="pricing-plan" attributes="name type size">
  <template>
    <div class="col-md-{{size}}">
      <div class="panel panel-{{type}}">
        <div class="panel-heading">
          <h3 class="panel-title">{{name}}</h3>
        </div>
        <div class="panel-body">
          <content></content>
        </div>
      </div>
    </div>
  </template>
  <script>
    Polymer('pricing-plan', {
      name: 'Plan',
      type: 'default',
      size: 4
    });
  </script>
</polymer-element>
With that, I can define my plans as:
<div id="price">
  <div class="container">
    <div class="col-md-10 col-sm-4">
      <pricing-plan name="Multi-Language">
        <ul><!-- ... --></ul>
      </pricing-plan>
      <pricing-plan name="I Only Like One">
        <ul><!-- ... --></ul>
      </pricing-plan>
      <pricing-plan name="Extras" type="primary" size="4">
        <ul><!-- ... --></ul>
      </pricing-plan>
    </div> <!-- class="col-md-10 col-md-offset-1 col-sm-4 " -->
  </div> <!-- /.container -->
</div> <!-- /#price -->
Which results in a nice list of plans:



Darn it. That was a little too easy. Darn you Polymer, I normally like to make things a little harder for myself. So I ask a bonus question tonight. How can I do this without requiring the web page to load the Bootstrap CSS and JavaScript? The answer, at least as far as I can tell from the FAQ is to add tags such that the browser can ignore dependedencies that are required multiple times.

Something like this seems to do the trick:
<polymer-element name="pricing-plan" attributes="name type size">
  <template><!-- ... --></template>
  <script>
    var ss = document.createElement("link");
    ss.type = "text/css";
    ss.rel = "stylesheet";
    ss.href = "css/bootstrap.min.css";
    document.getElementsByTagName("head")[0].appendChild(ss);

    Polymer('pricing-plan', {
      name: 'Plan',
      type: 'default',
      size: 4
    });
  </script>
</polymer-element>
(adapted from this StackOverflow solution)

Even if I add that <link> tag a hundred times, the browser should only make a single request for the duplicate resource. And, if I clear the browser cache and reload the page, that is exactly what I see:
127.0.0.1 - - [17/Nov/2013 23:54:16] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [17/Nov/2013 23:54:16] "GET /scripts/polymer.min.js HTTP/1.1" 200 -
127.0.0.1 - - [17/Nov/2013 23:54:17] "GET /pricing-plan.html HTTP/1.1" 200 -
127.0.0.1 - - [17/Nov/2013 23:54:17] "GET /scripts/polymer.min.js.map HTTP/1.1" 200 -
127.0.0.1 - - [17/Nov/2013 23:54:17] "GET /css/bootstrap.min.css HTTP/1.1" 200 -
That is not going to win me any style points, but I'll take it for now.


Day #938

Saturday, November 16, 2013

Polymer and Bootstrap


Tonight, I step a little outside my comfort zone and program in... JavaScript.

Well, OK, not too far outside my comfort zone, but since I turned 3D Game Programming for Kids over to my editor, it has been a good streak of pure Dart blogging. But, for my next book to come in Dart and JavaScript flavors, I necessarily need to have a good feel for coding Polymer in JavaScript. Since I have yet to do that, now seems a good time to start.

I like to start simple and dumb, but never seem to manage it. One could argue that those are the same things, but I manage to do both quite often, thank you very much! By "simple," I mean limiting newish features and coupling. By "dumb," I mean that I am probably going to be misusing Polymer. I think the <ice-code-editor> tag that I created in Dart was pretty cool, but it was pretty complicated given the underlying editor. Tonight, I start with <pricing-plans>, which will not be simple because of external coupling.

I wound up spending a lot of time on the pricing plans section of the Patterns in Polymer landing page. Since I have other books that I might like to sell in a similar fashion, maybe the <pricing-plans> tag isn't too dumb. But it probably is...

The thing that was giving me grief was fiddling with various Bootstrap panel class values:
<div id="price" class="list-spread-narrow">
  <div class="container">
    <div class="col-md-10 col-md-offset-1 col-sm-4 ">
      <div class="panel panel-default col-md-3">
        <div class="panel-heading">
          <h3 class="panel-title">Multi-Language</h3>
        </div>
        <div class="panel-body">
          <ul>
            <li>Get the <strong>JavaScript</strong> version!</li>
            <li>Get the <strong>Dart</strong> version!</li>
            <li>Private <strong>GitHub repository</strong> access to see how
            it's done.</li>
          </ul>
        </div>
      </div> <!-- /.panel1 -->
      <!-- More panels here -->
  </div> <!-- /.container -->
</div> <!-- /.list-spread-narrow -->
After a while, that stuff starts to get to me. Since this tends to be very repetitive, perhaps this is something that I can extract out? I have no real idea if it is a good idea to couple Bootstrap and Polymer, but there is one way to find out!

So I do the usual Polymer thing by including the library and a reference to the element that I am going to create in the <head> section of my HTML document:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="scripts/polymer.min.js"></script>
    <!-- 2. Load a component -->
    <link rel="import" href="pricing-plan.html">
  </head>
  <body>
  <!-- pricing plans here.... ->
  </body>
</html>
For my first pass, I define pricing-plan.html as:
<polymer-element name="pricing-plan" noscript>
  <template>
     <div class="panel panel-default col-md-3">
       <div class="panel-heading">
         <h3 class="panel-title">Multi-Language</h3>
       </div>
       <div class="panel-body">
         <content></content>
       </div>
     </div> <!-- /.panel1 -->
  </template>
</polymer-element>
I have pulled in the repetitive panel definition, but not the actual list of features. Those are specific to each panel, so I will need to include that content where the <content> tag is in my template. Back in the main document, I can use this tag as:
<pricing-plan>
  <ul>
    <li>Get the <strong>JavaScript</strong> version!</li>
    <li>Get the <strong>Dart</strong> version!</li>
    <li>Private <strong>GitHub repository</strong> access to see how
      it's done.</li>
  </ul>
</pricing-plan>
That is a lot cleaner and easier to follow. Especially given that I did not have too much work to make it happen:



The last thing that I would like to do tonight is the ability to specify the name of the pricing plan. For this one, it would be "Multi-language." For that, I need to drop the noscript declaration from my <polymer-element> definition. In its place, I need a very simple script that creates a simple Polymer object:
  <script>
    Polymer('pricing-plan');
  </script>
That allows me to declare a list of public attributes for my element. The only attribute that I need is name so my entire <polymer-element> becomes:
<polymer-element name="pricing-plan" attributes="name">
  <template>
     <div class="panel panel-default col-md-3">
       <div class="panel-heading">
         <h3 class="panel-title">{{name}}</h3>
       </div>
       <div class="panel-body">
         <content></content>
       </div>
     </div> <!-- /.panel1 -->
  </template>
  <script>
    Polymer('pricing-plan');
  </script>
</polymer-element>
Back in the main HTML document, I can make use of this as:
<pricing-plan name="Multi-Language">
  <ul>
    <li>Get the <strong>JavaScript</strong> version!</li>
    <li>Get the <strong>Dart</strong> version!</li>
    <li>Private <strong>GitHub repository</strong> access to see how
      it's done.</li>
  </ul>
</pricing-plan>
That is some simplified HTML!

I still might like the ability to make those columns a little wider or more narrow, depending on how many pricing plans I have. Ooh! And I still need to be able specify one as being the primary entry. Grist for tomorrow.



Day #937