Showing posts with label polymer. Show all posts
Showing posts with label polymer. Show all posts

Thursday, April 9, 2015

UI-less Polymer Elements as the Model in MVVM


Last night's attempt at MVVM (Model-View-ViewModel) in Polymer did not quite work out the way I hoped.

I am trying to rework the very simple <hello-you> Polymer element used in a couple of Patterns in Polymer chapters:



I had hoped that the simplicity would lead to a quick solution on which I could build something more complex. I found myself stuck on the Model. I think the class is simple enough. To support the element as-is, the most basic of objects ought to suffice:
function Person(name) {
  this.name = name ? name : '';
}
What tripped me up was marking name observable. If a human introduces themselves, the Person object should have its name property updated and the View should be able to observe that change so that it can update itself with the new name.

Last night's solution was to shelve the idea of a Person object and use a plain old Object instead. That works fine in Polymer since Polymer can observe plain old objects out of the box—its properties are automatically observable. That solves my simple case, but what about a more complex object with a prototype and methods?

I can think of at least three approaches that might work for this. Tonight, I try what I hope is the easiest: making the Model a UI-less Polymer element:
Polymer({
  is: 'x-person',
  properties: {
    name: {
      type: String,
      value: '',
      observer: 'nameChanged'
    }
  },

  nameChanged: function(v, old) {
    console.log(v + ' ' + old)
  }
});
This should give me an <x-person> element that I can use inside of <hello-you> to store the name of the Person being greeted: <x-person name="Chris"/>. I declare the UI-less template as:
<polymer-element name="x-person">
  <script src="x_person.js"></script>
</polymer-element>
I import this into the <hello-you> element with a <link> import:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="x-person.html">
<polymer-element name="hello-you">
  <template>
    <!-- Template HTML here ... -->
  </template>
  <script src="hello_you.js"></script>
</polymer-element>
With that, I should be able to use my <x-person> “Model” in the ViewModel (<hello-you>'s backing class) and the View (<hello-you>'s template).

The easiest (but possibly not the best for this pattern) way to bind <hello-you>'s <input> to the name property of <x-person> is in the template/view:
<polymer-element name="hello-you">
  <x-person name="{{name}}"></x-person>
  <template>
    <h2>Hello <span>{{name}}</span></h2>
    <p>
      <input value="{{name::input}}">
    </p>
    <!-- ... -->
  </template>
  <script src="hello_you.js"></script>
</polymer-element>
That is probably not quite right for MVVM since three items (<x-person>, the <h2> title, and the <input>) bind to a local name variable instead of a ViewModel property. I will worry about that later. For now, I have the ViewModel worrying about presentation (from last night) and a Model worrying about the “business” of naming.

Only this does not work.

When I update the <input>, the change observer that I logged in <x-person> is not seeing any changes. This turns out to be caused by placing <x-person> outside of <hello-you>'s <template> tag. The fix for this is easy enough, I move it inside of the <template>:
  <template>
    <x-person name="{{name}}"></x-person>
    <h2>Hello <span>{{name}}</span></h2>
    <p>
      <input value="{{name::input}}">
    </p>
    <!-- ... -->
  </template>
To complete tonight's efforts, I would like to drive the most basic of business logic in the Model: setting a default value. This is accomplished in Polymer 0.8 with a value property:
Polymer({
  is: 'x-person',
  properties: {
    name: {
      type: String,
      value: 'Bob',
      observer: 'nameChanged'
    }
  },
  // ...
});
But when I load up <hello-you>, I am not seeing “Bob”:



This is due to the lack of change notification from the Model. For a host element like <hello-you> to see changes to attributes of child elements, the child has to specify that the property generates notifications. This is done with the notify property:
Polymer({
  is: 'x-person',
  properties: {
    name: {
      type: String,
      value: 'Bob',
      notify: true,
      observer: 'nameChanged'
    }
  },
  // ...
});
Now, when I load my element, the name defaults to “Bob”:



I am unsure if this binding more properly belongs in the ViewModel than in the View as I have done here. For tonight, I claim a small victory in the form of an observable Model. It is clear that I need to continue my investigation into MVVM as I still do not have a complete handle on it.


Day #24

Wednesday, April 8, 2015

Circling Around MVVM in Polymer


I think MVVM is overkill for most Polymer elements. So tonight I apply it to an extremely simplistic Polymer element to confirm my bias.

I kid. I kid.

Several Patterns in Polymer readers have inquired about MVVM (Model-View-ViewModel) and I have replied that I think it likely overkill. But I have not really investigated—thoroughly or otherwise. I start with the simple case, not to quickly confirm my bias and move one, but to better help me get a grip on the necessary concepts. I tend to read about MV-whatever patterns and then lump them in the same place in my brain. Hopefully this will help.

The simple element remains the initial <hello-you> element in the book:



I continue to use last night's Polymer 0.8 code, mostly so I can continue to get a feel for the new stuff. I have the feeling this may, inadvertently, be a reasonable choice for MVVM (though it is still overkill). The “Fabulousize Me” button point to a method in the backing class that updates colors in the view. That sounds very much like view logic instead of business logic, which should push the code in a View Model.

But I'm getting ahead of myself...

The view in a Polymer element is simple enough. It is the <template> for the Polymer element. For the <hello-you> element, this is the view code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template>
    <h2>Hello <span>{{your_name}}</span></h2>
    <p>
      <input value="{{your_name::input}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
  <script src="hello_you.js"></script>
</polymer-element>
The your_name property comes directly from the model. The ViewModel can proxy it, but it is the purview of the Model. The your_name property is part of the business logic of <hello-you>. The feelingLucky() method on the other hand...

The backing class currently looks like:
var colors = ['red', 'blue', 'green'];
Polymer({
  is: 'hello-you',
  your_name: '',

  feelingLucky: function() {
    var num = Math.floor(colors.length*Math.random());
    this.
      querySelector('h2').
      style.color = colors[num];
  }
});
My first impression is that this is a ViewModel, not a Model. Certainly the feelingLucky() method is ViewModel code since it only interacts with the View. But if this is ViewModel, what does the Model look like?

In this case, I think it is nothing more than:
function Person(name) {
  this.name = name;
}
Again, this is a very simplistic Polymer element, so it makes sense that the Model is simple. As the code evolves, I could add first and last name properties, a full name computed method, and more. Before I get to that, how can I get this into the Polymer ViewModel? I kind of want to declare a you property as:
  // ...
  properties: {
    you: {
      type: Person
    }
  },
  //...
That will not work, however since properties can only be native types like String and Object. Well, the most basic model would be just an Object, so I try:
Polymer({
  is: 'hello-you',
  properties: {
    you: {
      type: Object
    }
  },
  // ...
}
In the View, I give direct access to this you Object:
    <h2>Hello <span>{{you.name}}</span></h2>
    <p>
      <input value="{{you.name::input}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
When I try to enter a name in the <input> field, however, I get:
Uncaught TypeError: Cannot set property 'name' of undefined
Hrm... even the simple Object version of the Model is not making this easy. Perhaps I need to initialize the you property?

I try this in the created() callback:
  // ...
  created: function(){
    this.you = {name: ''};
  }
  // ...
But I get the following when loading the element:
Uncaught SyntaxError: Unexpected identifier
I am unsure why that fails. I can get that working by changing to the ready() lifecycle method, but I eventually realize this is not how Polymer wants me to do this. I should be creating a default value in the properties declaration:
var colors = ['red', 'blue', 'green'];
Polymer({
  is: 'hello-you',

  properties: {
    you: {
      type: Object,
      value: {name: ''}
    }
  },

  feelingLucky: function() {
    var num = Math.floor(colors.length*Math.random());
    this.
      querySelector('h2').
      style.color = colors[num];
  }
});
With that, I have something moving toward MVVM. It does not feel like a real MVVM implementation—at least not without a method or two in the Model. Still, it is a baby step in the right direction. I am unsure how I want to get a real Model tomorrow, but I may start by creating another, UI-less Polymer object as the first try. If nothing else, this should make it easier to get observable properties and methods.


Day #23


Tuesday, April 7, 2015

Upgrading a Polymer Element to Polymer 0.8


Tonight, I break a rule. Sort of.

Normally in these chains, I think of a question to which I do not know the answer then do my best to answer it. During the course of exploring web-component-tester (WCT) last night, I was quite taken aback at the difficulty involved in upgrading a project from Polymer 0.5 to the new 0.8 (even with the very nice migration guide). I was able to get it working, but without much thought.

So tonight, I revert all of my code changes and start again:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working directory)

        modified:   code-js/polymer/elements/hello-you.html
        modified:   code-js/polymer/elements/hello_you.js
        modified:   code-js/polymer/index.html
        deleted:    code-js/polymer/karma.conf.js
        deleted:    code-js/polymer/test/HelloYouSpec.js
        deleted:    code-js/polymer/test/PolymerSetup.js

no changes added to commit (use "git add" and/or "git commit -a")
$ git checkout .
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

nothing to commit, working directory clean
I can get rid of the Karma tests as I am likely switching to WCT one way or another, but I leave them for now since my CI build expects them.

I will do this work in a new polymerjs-0_8 branch in the Patterns in Polymer book repository. After some thought (and discussion), I think it best for the book to target PolymerJS 0.5 for the time being. Targeting 0.8/0.9/1.0 can wait for the next edition. Still, I need to start playing with it, so let's try to repeat last night's success...

First up, I remove the "polymer": "<0.8" constraint that I added earlier today to my project's bower.json. In this branch, I will again target whatever the latest (semi-)stable version of Polymer is available:
{
  "name": "hello-you",
  // ...
  "dependencies": {
    "polymer": ">0.0"
  }
}
A Bower update and I am ready to roll:
bower update
....
polymer#0.8.0 bower_components/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.6.0

webcomponentsjs#0.6.0 bower_components/webcomponentsjs
My tests are failing, but before I even start with that, I update both my sample HTML page and my WCT test page to point to the new webcomponentjs:
    <!-- 1. Load the polymer platform. -->
    <script
       src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="elements/hello-you.html">
My tests are still failing all the same, but this is the most obvious external change required, so it makes sense to start with that.

Next, I start with making sure that my element compiles. I have a sanity test for just this sort of thing. In Polymer 0.5, I would check that my Polymer element had a shadow root element. In 0.8, I need to check for the existence of a "shady" root:
  describe('element content', function(){
    it('has a shady DOM', function(){
      assert.isDefined(el.root);
    });
  });
It does not:
chrome 41                ✖ index.html » <hello-you> » element content » has a shady DOM

  expected undefined to not equal undefined
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:10:0
If I look closer in a persistent WCT browser session, I find:
Uncaught TypeError: undefined is not a function     
desugar                                            polymer.html:37 
window.Polymer                                     polymer.html:21
(anonymous function)                               hello_you.js:2
This is because I can no longer declare my elements in Polymer with the name of the element as the first argument to the Polymer() function:
// 0.5 style:
Polymer('hello-you', {
  // Element definition here...
});
Instead, the element name ("hello-you" in this case) goes in the definition as the is property:
Polymer({
  is: 'hello-you',
  // The rest of the element definition here...
});
With that, I have my sanity test passing:
✓ index.html » <hello-you> » element content » has a shady DOM
One hurdle cleared, but another of my tests is not even registering and the last, which tests clicks, is failing:
✖ index.html » <hello-you> » clicking » randomly changes the title's color

  Cannot read property 'querySelector' of null
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:44:0
I start with the failing test because I can actually see it and, if last night's spike is any guide, the other test will magically reappear after this one is resolved. This is easy enough. I had been looking for child elements in my Polymer element's shadow DOM. There is no shadow DOM anymore (at least not by default), so I can query a little more directly in my tests now:
    it('randomly changes the title\'s color', function(){
      var button = el.querySelector('input[type=submit]');

      var e = document.createEvent('MouseEvent');
      e.initMouseEvent(
        "click", true, true, window,
        0, 0, 0, 80, 20,
        false, false, false, false, 0, null
      );
      button.dispatchEvent(e);

      var h2 = el.querySelector('h2');
      assert.equal(h2.style.color, 'green');
    });
My test still fails, but with a different message:
✖ index.html » <hello-you> » clicking » randomly changes the title's color

  expected '' to equal 'green'
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:56:0
I think I have a legitimate Polymer 0.8 test now, but it does not appear to be interacting with the randomizer button. Checking in the WCT persistent window, I find the following warning:
[input].[click]: event handler [{{feelingLucky}}] is null in scope
It would be nice if warnings like that showed up in the WCT console output because this tells me exactly what the problem is: I can no longer bind methods with the double-curly braces like that. Instead, I have to update my randomizing button to point to the feelingLucky() method with no braces at all:
<polymer-element name="hello-you">
  <template>
    <h2>Hello {{your_name}}</h2>
    <p>
      <input value="{{your_name}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
  <script src="hello_you.js"></script>
</polymer-element>
After that my test still fails to pass, but that is a simple code fix—the feelingLucky() method was also pointing to the shadowRoot. After removing that reference, I have two passing tests and... the last test is still missing. I am unsure why that would be, but it seems like a bug in WCT. If I check the console in the WCT persistent browser, I see that I am again using shadowRoot. After removing that from the test, I now get the failure that I desire:
✖ index.html » <hello-you> » typing » says a friendly hello

  expected 'Hello {{your_name}}' to equal 'Hello Bob'
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:33:0
Part of this is easy to solve. Bound variables now have to reside entirely inside of tag. So I update the Polymer template to place {{your_name}} inside a <span>:
  <template>
    <h2>Hello <span>{{your_name}}</span></h2>
    <p>
      <input value="{{your_name}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
The other thing that needs to change is the binding of the your_name property inside the <input> element—{{your_name}} by itself will no longer work. In 0.8, I have to append the kind of event that triggers an update to the property. In this case, I want your_name updated when an input event occurs:
  <template>
    <h2>Hello <span>{{your_name}}</span></h2>
    <p>
      <input value="{{your_name::input}}">
      <input type=submit
             value="Fabulousize Me!"
             on-click="feelingLucky">
    </p>
    <p class=help-block>
      <content></content>
    </p>
  </template>
With that, I have all three of my tests for this simple element passing:
✓ index.html » <hello-you> » element content » has a shady DOM
✓ index.html » <hello-you> » typing » says a friendly hello
✓ index.html » <hello-you> » clicking » randomly changes the title's color
And, more importantly, the <hello-you> element again appears to be working:



That was a lot saner than last night. The only thing that I did yesterday, but not today, was converting the <polymer-element> to <dom-element>. I was under the impression that it was required, but it would seem not.


Day #22

Monday, April 6, 2015

Replacing Karma with web-component-tester in Polymer Projects


It looks as though I will be replacing Karma with web-component-tester as the testing framework in Patterns in Polymer. I only really need to do the in the two testing chapters, but I'm in for a penny...

To get started, I will switch testing for the very simple <hello-you> Polymer element from chapter 1 of the book, then upgrade to the latest Polymer to see if anything breaks. That seems like a nice little test of things.

I start by simply replacing the karma dependencies in my chapter's package.json with web-component-tester (WCT):
{
  "name": "hello-you",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-watch": "~0.6.1",
    "grunt-notify": "^0.4.1",
    "web-component-tester": "^2.2.6"
  }
}
I remove the Karma configuration, the test/PolymerSetup.js that was needed in Karma, and the existing node_modules. Then I install the new WCT dependencies:
➜  polymer git:(master) rm -rf karma.conf.js test/PolymerSetup.js node_modules; npm install 
From there, I can convert my tests fairly easily. I had defined the test files in the old karma.conf.js. The web-component-tester expects the test entry point to reside in test/index.html. So I create test/index.html and point it the existing test file:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/hello-you.html">
</head>
<body>
  <hello-you id="fixture"></hello-you>
  <script src="index.js"></script>
</body>
</html>
I also add a fixture in there for easier access to the Polymer element that I want to test.

The tests themselves nearly work without change. I had been using Jasmine for my tests. The BDD interface supported by Chai is very similar to Jasmine—all I need to change is my assert statements:
describe('<hello-you>', function(){
  var el;

  beforeEach(function(){
    el = document.querySelector('#fixture');
  });
  // ...
  describe('typing', function(){
    beforeEach(function(done){
      var input = el.shadowRoot.querySelector('input');
      var e = document.createEvent('TextEvent');
      e.initTextEvent('textInput', true, true, null, 'Bob');
      input.dispatchEvent(e);
      setTimeout(done, 0); // One event loop for Polymer to process
    });

    it('says a friendly hello', function() {
      var h2 = el.shadowRoot.querySelector('h2');
      assert.equal(h2.textContent, 'Hello Bob');
    });
  });
});
Just like that, I have passing tests:
chrome 41                Beginning tests via http://localhost:59285/polymer/test/index.html?cli_browser_id=0
chrome 41                ✓ index.html » <hello-you> » element content » has a shadow DOM
chrome 41                ✓ index.html » <hello-you> » typing » says a friendly hello
chrome 41                Tests passed
Test run ended with great success

chrome 41 (2/0/0)

Done, without errors.
I have another test that relies on Jasmine spies. WCT supports spies, but Sinon stubs instead of Jasmine spies. In this case, I want to stub the return value of Math.random so that the otherwise random color change is deterministic. Sinon (like Jasmine) makes this easy:
  describe('clicking', function(){
    beforeEach(function(){
      // spyOn(Math, "random").and.returnValue(0.99);
      sinon.stub(Math, "random").returns(0.99);
    });

    it('randomly changes the title\'s color', function(){
      var button = el.shadowRoot.querySelector('input[type=submit]');

      var e = document.createEvent('MouseEvent');
      e.initMouseEvent(
        "click", true, true, window,
        0, 0, 0, 80, 20,
        false, false, false, false, 0, null
      );
      button.dispatchEvent(e);

      var h2 = el.shadowRoot.querySelector('h2');
      assert.equal(h2.style.color, 'green');
    });
  });
With that, I again have three passing tests for my simple element:
chrome 41                Beginning tests via http://localhost:37627/polymer/test/index.html?cli_browser_id=0
chrome 41                ✓ index.html »  » element content » has a shadow DOM
chrome 41                ✓ index.html »  » typing » says a friendly hello
chrome 41                ✓ index.html »  » clicking » randomly changes the title's color
chrome 41                Tests passed
Test run ended with great success

chrome 41 (3/0/0)                     

Done, without errors.
My next step is to upgrade the Polymer library to verify that (hopefully) nothing breaks. For the book, I have left Polymer unconstrained in Bower:
{
  "name": "hello-you",
  // ...
  "dependencies": {
    "polymer": ">0.0"
  }
}
So a simple bower update gets me to Polymer 0.8:
$ bower update
...
polymer#0.8.0 bower_components/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.6.0

webcomponentsjs#0.6.0 bower_components/webcomponentsjs
With that, I re-run my tests and find… that two tests are now failing:
chrome 41                Beginning tests via http://localhost:42253/polymer/test/index.html?cli_browser_id=0
chrome 41                ✖ index.html » <hello-you> » element content » has a shadow DOM

  expected null to not equal null
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:10:0

chrome 41                ✖ index.html » <hello-you> » clicking » randomly changes the title's color

  Cannot read property 'querySelector' of null
    <unknown> at   Context.<anonymous> at /polymer/test/index.js:44:0

chrome 41                Tests failed: 2 failed tests
Test run ended in failure: 2 failed tests

chrome 41 (0/0/2)                     
2 failed tests
At first, I suspect that there is a newer version of the web-component-tester that I need for Polymer 0.8. Then I take a closer look a the 0.8 Migration Guide and… wow. I am in some serious trouble with 0.8. Not only has the API completely changed, but many of the core concepts that have been baked in to Polymer for nearly two years have changed. Shady DOM? Ouch.

With not _too_ much effort, I am able to get my very simple element migrated to 0.8 and the tests passing again. But I need to give some very serious consideration to how I plan to handle 0.8 in the book. Given that it is only a preview release at this point, I may push to get the latest version of the book done sooner rather than later then deal with the repercussions of 0.8 after the new API stabilizes. Ugh. And I was having such a nice time with web-component-tester.


Day #21

Sunday, April 5, 2015

The Debugger and Breakpoints with web-component-tester


At this point, I am just looking for an excuse not to use web-component-tester. And so far, I have not found one. The web-component-tester (WCT) testing library for Polymer elements seems quite solid. It eliminates a lot of the overhead required for Polymer testing without sacrificing much in the way of configurability or extendability.

But, thanks to a question from Jim Simon in response to last night's post, I might just have the excuse for which I have been looking.

OK, in reality, I am not looking for a reason to dislike WCT. I am simply casting a very critical eye at it before I switch testing solutions in Patterns in Polymer to use it. Until Jim's question, I had yet to find a reasonable objection. Until Jim's question...

So here it is, how do you debug tests in WCT? With Karma, which is the current Patterns in Polymer recommendation for JavaScript, you click the Debug button on the page. Setting breakpoints and using debugger statements work as expected.

I would be satisfied that debugging was solid in WCT if I could simply get a debugger statement to work. So I add one to my Page Objects test from a couple of nights ago:
  describe('adding a whole topping', function(){
    beforeEach(function(done){
      xPizza.addWholeTopping('green peppers', done);
    });

    it('updates the pizza state accordingly', function(){
      var with_toppings = JSON.stringify({
        firstHalfToppings: [],
        secondHalfToppings: [],
        wholeToppings: ['green peppers']
      });

      debugger

      assert.equal(
        xPizza.currentPizzaStateDisplay(),
        with_toppings
      );
    });
  });
This seems a useful point to set a debugger statement. The element would have been established by virtue of a fixture and WCT's baked-in wait for Polymer.onReady. In this case, I use the page object's addWholeTopping() method to interact with the element, wait for changes to be done, then wait for the debugger statement to kick-in.

This will not work with my file-watcher setup because debugger statements, like breakpoints, only work if the JavaScript console is open in Chrome. Without that, my tests merrily continue to pass:
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 52328
Web server running on port 40424 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:40424/js/test/index.html?cli_browser_id=0
chrome 41                ✓ index.html »  » can add toppings to the whole pizza
chrome 41                ✓ index.html »  » defaults » has no toppings anywhere
chrome 41                ✓ index.html »  » adding a whole topping » updates the pizza state accordingly
chrome 41                ✓ simple_tests_01.html »  (1) » starts with no toppings
chrome 41                ✓ simple_tests_02.html »  (2) » starts with no toppings
chrome 41                Tests passed
Test run ended with great success

chrome 41 (5/0/0)                     

Done, without errors.
My first thought at this point is that I need to make use of WCT's persistent option:
$ wct --help

Usage: wct [options]

Options:
   -p, --persistent        Keep browsers active (refresh to rerun tests).
   --root                  The root directory to serve tests from.
   --plugin NAME           Plugins that should be loaded.
   --skip-plugin NAME      Configured plugins that should _not_ be loaded.
   --expanded              Log a status line for each test run
...
I give that a try:
$ wct -p
Starting Selenium server for local browsers
Selenium server running on port 43088
Web server running on port 56994 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:56994/js/test/index.html?cli_browser_id=0
chrome 41                ✓ index.html » <x-pizza> » can add toppings to the whole pizza
chrome 41                ✓ index.html » <x-pizza> » defaults » has no toppings anywhere
chrome 41                ✓ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly
chrome 41                ✓ simple_tests_01.html » <x-pizza> (1) » starts with no toppings
chrome 41                ✓ simple_tests_02.html » <x-pizza> (2) » starts with no toppings
...
The tests continue to pass (and not break for debugging). But the test browser remains. I open the JavaScript console:



Then I reload the page, and... the dang page closes the JavaScript console before re-running the tests. My guess is that WCT is doing this because this browser session is still attached to the backend WCT session. When I reload the browser, the WCT session again reports:
...
chrome 41                Beginning tests via http://localhost:56994/js/test/index.html?cli_browser_id=0
chrome 41                ✓ index.html » <x-pizza> » can add toppings to the whole pizza
chrome 41                ✓ index.html » <x-pizza> » defaults » has no toppings anywhere
chrome 41                ✓ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly
chrome 41                ✓ simple_tests_01.html » <x-pizza> (1) » starts with no toppings
chrome 41                ✓ simple_tests_02.html » <x-pizza> (2) » starts with no toppings
But what would happen if I start a new window at the same URL, but without that cli_browser_id query string parameter?

There is an easy way to find the answer to that. I open a new Chrome window, open its JavaScript console, and point it to my current WCT server (currently for me at http://localhost:56994/js/test/index.html). With that, I see:



Yay! Debugging.

I can do anything that I normally would in there: look at local variables, check the call stack, step through execution, even interact with objects in the console:



This works just as well with debugger statements in the actual Polymer code and with breakpoints set in the Sources tab. This seems exactly what I need (and hopefully what Jim needs as well).

I think that I will continue to run a file watcher WCT instance during the course of normal development. When things go really wrong, I will stop the WCT instance and bring up a --persistent instance of WCT and a completely separate Chrome window in which I can do the debugging thing. Once I resolve the problem, I can close the persistent WCT session and browser windows, then return to my regularly scheduled file watcher session.

I am rapidly running out of reasons to dislike WCT. But there is always hope for tomorrow.


Day #20

Saturday, April 4, 2015

Better Reporting and Errors in web-component-tester


The sad path with web-component-tester is a little worrisome. So far, web-component-tester (WCT) has handled pretty much everything I had to throw at it with aplomb. It seems every bit a well thought out and implemented test framework for Polymer. But I have mostly worked with successful tests—the happy path. On occasion, something unexpected went wrong and I had trouble figuring out where. That bothered me enough to note, but at the time, I focused on the task at hand.

Now, I look closer at failures—the sad path.

I start by breaking a test intentionally so that I can take a closer look at what WCT reports:
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 33554
Web server running on port 36678 and serving from /home/chris/repos/polymer-book/play/web-component-tester
firefox 37               Beginning tests via http://localhost:36678/js/test/index.html?cli_browser_id=0
firefox 37               ✖ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly

  expected '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["green peppers"]}' to equal '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["red peppers"]}'

firefox 37               Tests failed: 1 failed tests
Test run ended in failure: 1 failed tests

firefox 37 (4/0/1)                    
1 failed tests
I have 5 tests scattered across 4 test files. Which one broke? I know the answer in this case because I intentionally broke it, but had I been merrily coding a separate feature when greeted by this there is no indication of where the failure occurred. My only option would be to hunt for the test description. That would be incredibly tedious if I have multiple tests with the same description (e.g. in different contexts).

I note that using Chrome instead of Firefox will report the bad line number:
chrome 41                ✖ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly

  expected '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["green peppers"]}' to equal '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["red peppers"]}'
    <unknown> at   Context.<anonymous> at /js/test/page_objects.js:79:0
That can be easy to miss—especially if I am running more than one browser at a time. So I hope to get richer information from WCT with all browser.

WCT's --verbose option is not the answer. That is a swamp of output that, if anything, makes it harder to find the error. Instead, I try the --expanded option by adding it to my project's wct.cong.js file:
module.exports = {
  expanded: true,
  plugins: {
    local: {
      browsers: ['firefox']
    }
  }
};
Not surprisingly, this results in expanded output:
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 57440
Web server running on port 53285 and serving from /home/chris/repos/polymer-book/play/web-component-tester
firefox 37               Beginning tests via http://localhost:53285/js/test/index.html?cli_browser_id=0
firefox 37               ✓ index.html » <x-pizza> » can add toppings to the whole pizza
firefox 37               ✓ index.html » <x-pizza> » defaults » has no toppings anywhere
firefox 37               ✖ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly

  expected '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["green peppers"]}' to equal '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["blue peppers"]}'

firefox 37               ✓ simple_tests_01.html » <x-pizza> (1) » starts with no toppings
firefox 37               ✓ simple_tests_02.html » <x-pizza> (2) » starts with no toppings
firefox 37               Tests failed: 1 failed tests
Test run ended in failure: 1 failed tests

firefox 37 (4/0/1)                    
1 failed tests
That is of some use, but is not entirely satisfactory. I had not realized earlier that index.html in the output referred to the HTML context of the output. That is the entry point for all of my tests, so I assumed that any failure would be reported from there. In fact, alternate HTML contexts, such as simple_tests_01.html are reported as distinct from the JS tests.

All of my tests are loaded from separate files in index.html:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    WCT.loadSuites([
      'simple_tests_01.html',
      'adding_toppings.js',
      'page_objects.js',
      'simple_tests_02.html',
    ]);
  </script>
</body>
</html>
The JS tests are all reported as coming from index.html while the HTML tests are reported as coming from their respective HTML files. With that understanding, I think I can live with this behavior.

Between knowing that I can find the actual line number failure in the output from Chrome and that all browsers will report the correct HTML context, I ought to be fine. For the most part, I imagine that I will just run Chrome tests for development (to keep the runs shorter) and run multiple browsers in continuous integration.

Furthermore, I think it very unlikely that I will ever use multiple JS test suites for a single HTML context like I have done here. I prefer my JS in a separate file, so it is likely that for each simple_tests_01.html and simple_tests_02.html, each will load a corresponding simple_tests_01.js and simple_tests_02.js. So if a test reports failure from a given HTML context, I will know where to start looking. I doubt that I wound ever use two JS contexts as I have done here. As I found last night, doing this makes it easy to confuse your test environment.

So one JS suite for each HTML context will likely resolve my objections. Another potential objection to WCT gone. One way or another, I am definitely sticking with the expanded option. The resulting output may not be as nice as Mocha's spec reporter, but it is close enough for my tastes:
firefox 37               ✓ index.html » <x-pizza> » can add toppings to the whole pizza
firefox 37               ✓ index.html » <x-pizza> » defaults » has no toppings anywhere
firefox 37               ✓ index.html » <x-pizza> » adding a whole topping » updates the pizza state accordingly
firefox 37               ✓ simple_tests_01.html » <x-pizza> (1) » starts with no toppings
firefox 37               ✓ simple_tests_02.html » <x-pizza> (2) » starts with no toppings
firefox 37               Tests passed
Test run ended with great success
It is looking more and more like I need to update a few chapters in Patterns in Polymer.


Day #19

Friday, April 3, 2015

Page Objects and web-component-tester


I am a Page Objects fan. I like page objects for testing web pages, applications, and even Polymer. Tonight, I try to use them with the web-component-tester (WCT).

I typically eschew code reuse in my tests. That is not quite strong enough. More accurately, indirection in tests inspires feelings of anger, bordering on rage. I prefer my test code to be right there in the tests, not scattered around a bunch of files. When something is misbehaving in tests, I loathe searching test code to find the problem.

I make an exception in the case of page objects because they are built primarily around user interactions with applications—code reuse is simply a (tolerable) byproduct. Putting myself in the frame of mind of a human interacting with my applications is where I find value.

I start by adding a new suite to last night's multi-suite WCT for the tasty <x-pizza> Polymer element:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    WCT.loadSuites([
      'simple_tests_01.html',
      'adding_toppings.js',
      'page_objects.js',
      'simple_tests_02.html',
    ]);
  </script>
</body>
</html>
I have every reason to expect that page objects will work with WCT. Happily, I am not disappointed. I mostly copy my Karma and Jasmine implementation of Page Objects for this element.

The constructor takes a single element argument:
function XPizzaComponent(el) {
  this.el = el;
}
The methods supported for this element facilitate adding toppings as a human might and reading the results:
XPizzaComponent.prototype = {
  currentPizzaStateDisplay: function() {
    if (this.el.$ === undefined) return '';
    return this.el.$.state.textContent;
  },

  addWholeTopping: function(v, cb) {
    // Select topping from drop-down and click a button here...
    return this.flush(cb);
  },

  flush: function(cb) {
    if (cb === undefined) cb = function(){};
    this.el.async(cb);
  }
};
The most important part of this is the flush() method, which accepts a callback parameter. It invokes Polymer's async() method to ensure that all bound variables are updated and that the UI is updated. Once everything has changed, the supplied callback is invoked. It is useful to invoke flush() directly from tests, but even more useful to invoke it from tests that update the UI—as the addWholeToppings() method does.

This is important for asynchronous testing of Polymer elements. I can supply Mocha's done callback to flush() so that subsequent tests will not run until the element has finished updating itself. So, to test that I can update whole toppings, I call the addWholeTopping() method of the page object and supply the done callback:
  describe('adding a whole topping', function(){
    beforeEach(function(done){
      xPizza.addWholeTopping('green peppers', done);
    });

    it('updates the pizza state accordingly', function(){
      var with_toppings = JSON.stringify({
        firstHalfToppings: [],
        secondHalfToppings: [],
        wholeToppings: ['green peppers']
      });

      assert.equal(
        xPizza.currentPizzaStateDisplay(),
        with_toppings
      );
    });
  });
The test itself will not run until the element has completely updated its properties, bound variables, and UI. This is perfect because the test is trying to assert that the element has been updated to include pepperoni.

This works exactly as desired:
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 58383
Web server running on port 39429 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:39429/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 37               Beginning tests via http://localhost:39429/js/test/index.html?cli_browser_id=1
firefox 37               Tests passed
Test run ended with great success

chrome 41 (5/0/0)                       firefox 37 (5/0/0)                    

Done, without errors.
I am growing fond of WCT. It supports the various kinds of testing that I might want. I still worry that there might be some deficiencies that might give me pause for using it today in Patterns in Polymer, so I will continue to explore tomorrow.


Day #18

Thursday, April 2, 2015

Organizing web-component-tester Test Suites


I like web-component-tester. I just don't know if I like like it.

Actually, I probably do, but I am unsure if I like it enough to go back and rewrite testing chapters in Patterns in Polymer. Certainly I would prefer the option with less work, but not at the expense of an inferior solution. So tonight, I press on in my investigation.

Having gotten my Grunt watcher configuration just right, tonight I experiment a little with the test organization options that are supported by web-component-tester (WCT). I start by simply copying my existing test into separate JS files. The existing test is contained in my project's test/index.html page:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    suite('<x-pizza>', function() {
      test('starts with no toppings', function() {
        assert.equal(
          document.getElementById('fixture').pizzaState,
          '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
        );
      });
    });
  </script>
</body>
</html>
To load tests, I replace the test suite above with the WCT.loadSuites() method:
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    WCT.loadSuites([
      'simple_tests_01.js',
      'simple_tests_02.js',
    ]);
  </script>
</body>
Here, I load two suites which, as the names imply, are very simple. I also retain the <x-pizza> fixture in place in this test/index.html entry point (this may be a mistake, but I leave it for now). The simple tests copy the original test which verifies the initial state of the element:
suite('<x-pizza>', function() {
  test('starts with no toppings', function() {
    assert.equal(
      document.getElementById('fixture').pizzaState,
      '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
    );
  });
});
With that, my WCT tests still pass. Thanks to my duplicated test, I now have two passing tests in each of the browsers for which WCT is configured:
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 48572
Web server running on port 48270 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:48270/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 37               Beginning tests via http://localhost:48270/js/test/index.html?cli_browser_id=1
firefox 37               Tests passed
Test run ended with great success

chrome 41 (2/0/0)                       firefox 37 (2/0/0)                    

Done, without errors.
I am unsure of the best practice for fixture elements. I have the feeling that once a test alters the state, subsequent test suites will see the change instead of the initial, default state. To test that theory, I add a new test that adds a topping to the <x-pizza> element:
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    WCT.loadSuites([
      'simple_tests_01.js',
      'adding_toppings.js',
      'simple_tests_02.js',
    ]);
  </script>
</body>
The test itself looks something like:
suite('<x-pizza>', function() {
  var el;

  setup(function(done){
    el = document.getElementById('fixture');
    // Crazy JS to select an item from a drop-down and trigger a
    // change here...

    el.async(done);
  });

  test('can add toppings to the whole pizza', function() {
    assert.equal(
      el.pizzaState,
      '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["pepperoni"]}'
    );
  });
});
The craziness that updates the element is best left for another day. For now, please take it on faith that the elided code makes a change to the <x-pizza> element. And indeed it does since this test passes. Unfortunately, as I feared, it changes the <x-pizza> element for later tests because the last simple test is now failing due to the presence of pepperoni:
...
firefox 37               ✖ index.html » <x-pizza> » starts with no toppings

  expected '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["pepperoni"]}' to equal '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'

firefox 37               Tests failed: 1 failed tests
Test run ended in failure: 1 failed tests

chrome 41 (2/0/1)                       firefox 37 (2/0/1)      
So, to isolate fixtures, it seems that external test suites need to be HTML suites. I switch simple_test_02.js to simple_test_02.html in the test/index.html entry point:
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    WCT.loadSuites([
      'simple_tests_01.js',
      'adding_toppings.js',
      'simple_tests_02.html',
    ]);
  </script>
</body>
And in that HTML suite, I start a completely new WCT test context for my <x-pizza> element. I load WCT, Polymer and import the element itself. Finally I write the test:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
<x-pizza id="fixture"></x-pizza>
<script>
suite('<x-pizza>', function() {
  test('starts with no toppings', function() {
    assert.equal(
      document.getElementById('fixture').pizzaState,
      '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
    );
  });
});
</script>
</body>
</html>
With that, I have all of my tests passing again:
>> File "test/simple_tests_02.html" changed.
Running "notify_hooks" task

Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 34160
Web server running on port 54777 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:54777/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 37               Beginning tests via http://localhost:54777/js/test/index.html?cli_browser_id=1
firefox 37               Tests passed
Test run ended with great success

chrome 41 (3/0/0)                       firefox 37 (3/0/0)                    

Done, without errors.
It seems safe to say that, if tests rely on fixture data like this, then HTML suites are the way to go. This is perfectly reasonable and not entirely unexpected. Still, it might improve maintainability if WCT can come up with a way to not duplicate the <head> section of each HTML suite. That minor quibble aside, test organization in WCT seems quite easy.


Day #17

Wednesday, April 1, 2015

Headless Polymer Testing with web-component-tester


The browser support in web-component-tester (WCT) is super cool. At first.

But after you run the test suite a few times—or automate it with file watchers—it quickly becomes super annoying. Any browsers that are configured to run tests pop up each time the test suite is run. And if the test suite is scheduled to run each time a change is made to the code, this behavior rapidly grows obnoxious.

So I would like to mitigate or prevent the browsers from popping when the tests are running. I start with mitigation since it is built into WCT via the persistent option. I mark the local WCT task that I am using as persistent in my Gruntfile.js:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': {
      local: {
        options: {remote: false, persistent: true}
      },
      // ...
    },
    // ...
  });
};
That does not quite work like I hope however. The browsers in which the tests are run are indeed persistent, but they are so persistent that they prevent the WCT task from completing. This leaves me with two windows that have the last state of the element being tested:



And my Grunt watch task is locked after beginning my tests in Chrome and Firefox:
$ grunt watch     
Running "notify_hooks" task

Running "watch" task
Waiting...
Running "notify_hooks" task

Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 47127
Web server running on port 43597 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:43597/js/test/index.html?cli_browser_id=0
firefox 37               Beginning tests via http://localhost:43597/js/test/index.html?cli_browser_id=1
No matter, those persistent WCT grapes were sour anyway.

Instead I am really hoping to get Xvfb working. If I fire up a virtual window system in which the browsers can run the tests, then I never have to see browsers. That should be perfect (and much better than the persistent grapes).

In fact, thank to xvfb-run, this is trivial. All I have to is start my Grunt task with that wrapper script:
$ xvfb-run -a grunt watch
Running "notify_hooks" task

Running "watch" task
Waiting...
Running "notify_hooks" task

Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 42548
Web server running on port 35453 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:35453/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 37               Beginning tests via http://localhost:35453/js/test/index.html?cli_browser_id=1
firefox 37               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 37 (1/0/0)                    

Done, without errors.
Completed in 7.450s at Thu Apr 02 2015 00:36:05 GMT-0400 (EDT) - Waiting..
So I have a passing test and I never have to see a single browser. If I make changes, the test suite is re-run in multiple browsers and I never see a single window.

I very much appreciate xvfb-run's -a option to automatically select a server port. If I have another session running on :99 (the default), xvfb-run will automatically chose an alternate port. Unfortunately, this has the side effect of not working with existing solutions for using Xvfb from within Grunt.

I can probably live with typing xvfb-run -a each time before starting the file-watching WCT test suite. But if I want to keep the command-line to a bare minimum, I can use grunt-shell:
$ npm install grunt-shell --save-dev
Then, in Gruntfile.js, I can load the shell tasks and configure my own:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    // ...
    shell: {
      test: {
        command: 'xvfb-run -a grunt watch'
      }
    },
    // ...
  });

  grunt.registerTask('default', ['shell:test']);

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-shell');
  grunt.loadNpmTasks('grunt-notify');
  grunt.task.run('notify_hooks');
};
If I make the new shell:test the default task (as above), I can start up a file-watching, headless WCT test suite with nothing more than:
$ grunt
Nice! Those are some tasty testing grapes.


Day #16

Tuesday, March 31, 2015

Automated Polymer Testing with Grunt and web-component-tester


Today, I try my hand at combining Grunt and web-component-tester for local Polymer development.

Last night's experiment with Gulp was reasonable successful. Initial setup with Gulp and web-component-tester (WCT) was brilliantly easy. Even the first bits of customization was relatively simple. Quickly, however, I reached a point where I was hip-deep in node.js streams, error handling and the alphabet soup of NPM packages that Gulp uses to make sense of them.

Grunt will certainly require a little more initial effort, but I am all-but-certain that I can get my complete local setup in place. The best laid plans....

What I want is a means to automatically run my tests locally whenever I make a change to code or test files. I also a appreciate a little LiveReload support for my sample pages. I have done both with Grunt before, though not with web-component-tester.

I start by wiping my sample application's package.json file so that I can re-add Grunt, web-component-tester, and a couple of other dependencies that I know that I will want to use:
$ npm install grunt grunt-notify  grunt-contrib-watch web-component-tester --save-dev
This leaves me with a package.json that contains:
{
  "name": "wct_sample",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-notify": "^0.4.1",
    "web-component-tester": "^2.2.6"
  }
}
I start my Gruntfile.js configuration file with the sample included in the WCT homepage:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': {
      local: {
        options: {remote: false}
      },
      remote: {
        options: {remote: true}
      },
      chrome: {
        options: {browsers: ['chrome']}
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
};
That gives me wct-test:local, wct-test:remote, and wct-test:chrome tasks. The first is the one that will run my test suite (currently just a single test):
$ grunt wct-test:local:chrome
Running "wct-test:local:chrome" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 33822
Web server running on port 35707 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:35707/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:35707/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Nice. That was just as easy to get running as the Gulp version (admittedly there is less configuration with Gulp for the very simple case).

Next, I would like to run my test suite whenever there is a change to any file in the tests or elements directories. This is why I included the grunt-contrib-watch development dependency at the outset. I update Gruntfile.js to include the watch tasks and definitions:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': {
      local: {
        options: {remote: false}
      },
      // ...
    },
    watch: {
      code: {
        files: ['index.html', 'elements/**'],
        options: {
          livereload: true
        }
      },
      tests: {
        files: ['elements/**', 'test/**'],
        tasks: ['wct-test:local'],
        options: {atBegin: true}
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
};
The watch:code task will watch the specified files which impact the sample page. On update, they trigger the browser to LiveReload the page (if the extension is being used). The watch:tests task will watch code and test files for changes, running the WCT's wct-test:local task when it sees those changes. For good measure, I specify that the tests should be run at start as well.

And that all works nicely. When I invoke watch, my tests are run right away:
$ grunt watch
Running "watch" task
Waiting...
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 44706
Web server running on port 37026 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:37026/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:37026/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Completed in 7.828s at Tue Mar 31 2015 23:37:56 GMT-0400 (EDT) - Waiting...
This task does not end, but polls for changes to the specified files. If I make a change to one of them, I find:
>> File "test/index.html" changed.
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 49367
Web server running on port 41348 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:41348/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:41348/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Completed in 7.491s at Tue Mar 31 2015 23:38:18 GMT-0400 (EDT) - Waiting...
To complete my desired setup, I would like to have desktop notifications fire at the end of each test run. This is where the grunt-notify package comes in handy. I want to use "notify hooks” in this case. Whenever a task completes successfully, these hooks will send a desktop notification with the result message.

To use hooks, I have to load the grunt-notify tasks as usual, then run the hooks task:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({ /* ... */ });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');
  grunt.task.run('notify_hooks');
};
That is all that is needed, but I like to specify two configuration options: one to send notifications when the tests succeed (only failure messages are displayed by default) and one to display the message for 8 seconds (instead of the default 3):
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': { /* ... */ },

    watch: { /* ... */ },

    notify_hooks: {
      options: {
        success: true,
        duration: 8
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');
  grunt.task.run('notify_hooks');
};
With that, I see a nice success message when I start Grunt:



And, if I break something, I see:



I might like to see the actual failure message in there, but that is a minor quibble (and a byproduct of WCT since I saw just the one line in Gulp as well). That aside, this is exactly what I want in my test setup and should serve nicely for actual development of Polymer code—something that I will put to the test over the next few days.


Day #15


Monday, March 30, 2015

Gulp and web-component-tester


I hope to build on last night's success with web-component-tester. With relatively little effort or confusion, I was able to write a simple-yet-legitimate test with this newish testing tool for Polymer. Tonight, I would like explore the automated test watching facilities.

First up, I will try Gulp for automated test watchers. I have never used Gulp before, mostly because the "Try Now" button on the homepage takes me to an install page instead of a live demo. I am easily confused by such things, but today I push through and follow the installation instructions so that I can then try.

Global and local install works fine:
$ npm install --global gulp
$ npm install --save-dev gulp
Next, I create a gulpfile.js as recommended on the web-component-tester page:
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);
That fails because I do not have web-component-tester installed locally. The wct instructions only mention globally installing it, but it is clearly needed locally for Gulp. So I install it:
$ npm install --save-dev web-component-tester
With that, my package.json now contains:
{
  "name": "wct_sample",
  "devDependencies": {
    "gulp": "^3.8.11",
    "web-component-tester": "^2.2.6"
  }
}
More importantly, my gulplfile.js works and I can run last night's test:
gulp test:local
[23:52:52] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[23:52:52] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 45226
Web server running on port 33654 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:33654/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:33654/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0) 
That is of little use to me, however. The wct command-line from last night does the same exact thing without extra installation and configuration. What I want is a file watcher that will run the test:local task whenever my Polymer code or tests change.

For that, I create a default task in gulpfile.js that establishes a gulp.watch():
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);

gulp.task('default', function() {
  gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
Now I can start gulp with no command-line arguments and it will watch for changes:
$ gulp
[00:14:48] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:14:48] Starting 'default'...
[00:14:48] Finished 'default' after 11 ms
...
If I make a change to an of the files being watched, my test runs:
$ gulp
[00:14:48] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:14:48] Starting 'default'...
[00:14:48] Finished 'default' after 11 ms
[00:16:21] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 54671
Web server running on port 42314 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:42314/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:42314/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    
[00:16:28] Finished 'wct:local' after 6.29 s
[00:16:28] Starting 'test:local'...
[00:16:28] Finished 'test:local' after 16 μs
I prefer for my test suite to run when I first start a test watched task. To get that to work, I add test:local as a dependency for the default task:
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);

gulp.task('default', ['test:local'], function() {
  gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
Now my tests run as soon as I start the default watcher:
$ gulp
[00:24:52] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:24:52] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 47399
Web server running on port 60784 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:60784/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:60784/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    
[00:24:58] Finished 'wct:local' after 6.1 s
[00:24:58] Starting 'test:local'...
[00:24:58] Finished 'test:local' after 18 μs
[00:24:58] Starting 'default'...
[00:24:58] Finished 'default' after 13 ms
The only thing left is popup notifications. If there is an easy way to get that working, it escapes me. Upon first pass at Gulp, the streams and events and all are a tad bewildering to me. So in the end, I fall back to manually installing node-notifier:
$ npm install --save-dev node-notifier
From there, I drop the web-component-tester Gulp tasks and directly invoke the testing tasks. If those fail, the I manually send notifications on error:
var gulp = require('gulp');
// require('web-component-tester').gulp.init(gulp);
var wct = require('web-component-tester').test;
var notifier = require('node-notifier');

gulp.task('default', function() {
  gulp.watch('{elements,test}/**', function(done) {
    wct({plugins: {local: {}, sauce: false}}, function(error){
      if (error) {
        notifier.notify({
          'title': 'Build failed',
          'message': '' + error
        });
      }
      else {
        notifier.notify({'title': 'Build passed', 'message': 'Yay!'});
      }
    });
  });
});
That is pretty ugly. It works, but here has to be a simpler way to accomplish. For me, the answer is likely going to be Grunt, which I already use elsewhere in Patterns in Polymer. Unless someone else has an easy way to get this working cleanly in Gulp?


Day #14

Sunday, March 29, 2015

My First web-component-tester Test


Next up in my research-while-awaiting-copy-edits checklist is the web-component-tester. This is probably the most requested topic about which folks have inquired as they read through this blog and the resulting Patterns in Polymer, so this is a great time to investigate.

For better or worse, I use Karma and Jasmine in the book. When I first got started with testing Polymer elements, there were no testing solutions (well Polymer.dart had some, but nothing in JavaScript land). Over time, the solutions that popped up or were suggested did not sit right with me for one one reason or another. Hopefully web-component-tester (wct) will break that trend and, based on the project README, I have every expectation that it will.

I have Java available in my command line $PATH:
$ java -version
java version "1.7.0_75"
OpenJDK Runtime Environment (IcedTea 2.5.4) (7u75-2.5.4-1~trusty1)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
So I should be able to install the tool with the usual global node.js NPM install:
npm install -g web-component-tester

> ws@0.5.0 install
...
> sauce-connect-launcher@0.9.3 postinstall
...
> wct-sauce@1.2.0 postinstall
...
> wct-local@1.3.1 postinstall
...
-----
selenium-standalone installation finished
-----
...
/home/chris/local/node-v0.10.20/bin/wct -> /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester/bin/wct
/home/chris/local/node-v0.10.20/bin/wct-st -> /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester/bin/wct-st
web-component-tester@2.2.6 /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester
...
I am using Bower to install Polymer—my bower.json for this sample project is:
{
  "name": "wct_example",
  "dependencies": {
    "polymer": "Polymer/polymer"
  }
}
Since there is no configuration (e.g. via .bowerrc), Polymer and the associated webcomponentjs package gets installed in the bower_components directory. I am testing a favorite from Patterns in Polymer, <x-pizza>, whose definition resides in the elements subdirectory. I adapt the setup in the sample wct page accordingly:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <!-- <script src="../../webcomponentsjs/webcomponents.min.js"></script> -->
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    // Tests will go here...
  </script>
</body>
</html>
I am unsure why the sample given on the project page uses the "../../webcomponentsjs/webcomponents.min.js" location for the webcomponents library. In my sample, that resides outside of the project directory entirely. Regardless, if this file is stored in test/index.html, then the "../bower_components/webcomponentsjs/webcomponents.min.js" location works for me. Additionally, I <link> import the <x-pizza> element definition and establish it as a fixture for my tests.

Next up, I write a simple test of <x-pizza>. To start, I would simply like to assert that it starts with no toppings:
    suite('<x-pizza>', function() {
      test('starts with no toppings', function() {
        assert.equal(
          document.getElementById('fixture').pizzaState,
          '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
        );
      });
    });
Amazingly, that works:
$ wct test/index.html
Starting Selenium server for local browsers
Selenium server running on port 35270
Web server running on port 33741 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:33741/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:33741/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0) 
I realize that was a very simple test, but I am extremely happy with how easy it was to get wct setup and to run my first test. I do not have a strong opinion on Jasmine vs. Mocha/Chai—they are fairly interchangeable in my mind. So all in all, this seems a nice win for testing—especially since wct hides any Polymer/ready waiting. The proof will come with more complicated tests, on which I start tomorrow.


Day #13

Saturday, March 28, 2015

Debouncing with scheduleJob in Polymer.dart


I make these little notes for myself and then I have no idea what they mean.

While awaiting the copy edits for the latest Patterns in Polymer, I have been reviewing research tasks that I have assigned myself. I have a pretty good handle on the challenges of the <google-map> API (especially in Dart), so I have checked that off. Next on the list is “native Polymer debounce.” Since Polymer tends to have as many polyfills as it has native components, I doubt the debounce is native, but let's see...

Mercifully, Michael Bleigh was king enough to use the word “debounce” on his post about Polymer Features That You May Have Missed. And indeed, I missed that one, which is called job().

At first blush, job() seems fairly straight-forward in the JavaScript sample from the article, so I'll have a go at it in Polymer.dart. Oddly enough, I have a debounce example in Patterns in Polymer, but it is only in the Dart version of the book. Using 3rd party libraries is so easy in Dart that I wrote that chapter about using 3rd party JavaScript libraries in Polymer.dart. The example in the book is using the Underscore debounce() method:
class HelloYou extends PolymerElement {
  // ...
  HelloYou.created(): super.created();

  var _debouncedLucky;
  feelingLucky() {
    if (_debouncedLucky == null) {
      _debouncedLucky = context['_'].
        callMethod('debounce', [_feelingLucky, 750, true]);
    }
    _debouncedLucky.apply([]);
  }

  _feelingLucky() { /* ... */ }
}
The feelingLucky() method is bound to a button in the template. The private _feelingLucky() Dart method is debounced by Underscore thanks to js-interop.

If I am correct, I believe that I can replace all of that with scheduleJob():
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  HelloYou.created(): super.created();
  // ...
  PolymerJob _debouncedLucky;
  feelingLucky() {
    _debouncedLucky = scheduleJob(_debouncedLucky, _feelingLucky, 750);
  }

  _feelingLucky() { /* ... */ }
}
Of course, I am not correct. The debounce duration needs to be supplied as a Duraction, not an integer number of seconds. Dart complains as much:
Exception: Uncaught Error: Class 'int' has no instance getter 'inMilliseconds'.
To supply the required Duration, I declare a delay duration of 750 milliseconds and supply that to scheduleJob():
final delay = const Duration(milliseconds: 750);

@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  HelloYou.created(): super.created();
  // ...
  PolymerJob _debouncedLucky;
  feelingLucky() {
    _debouncedLucky = scheduleJob(_debouncedLucky, _feelingLucky, delay);
  }

  _feelingLucky() { /* ... * }
}
With that, I can click the feeling lucky button as many times as I like in the UI, but, unless I wait 750 milliseconds between clicks, nothing happens. Once I wait, my simple <hello-you> element tries to guess my lucky color while saying howdy:



That is a handy little feature that I had overlooked. Well, not overlooked so much as deferred, but thankfully I can mention this by name in the book now.


Day #12