If a codebase's tests don't run regularly, the codebase is not tested. A team can spend hundreds of person-hours writing hundreds of tests. But if those tests are not executed at least once a day, the team might just as well delete them. They occupy space with no purpose.
This seems utterly obvious, yet I see it time and time again. And as much as I detest this poor excuse for programming, I am guilty of no less with the Patterns in Polymer project code. As I work through each chapter adding at least rudimentary Karma / Jasmine tests to the custom Polymer elements therein, I am surprised… at how many tests I actually did write.
And so far, the tests have been easy to fix once I updated the element definitions to the latest Polymer library. Until tonight. Tonight, I find that some of the console logging that I expect from a Polymer observer is not being called:
$ karma start
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket wjIrCJXi9b1e1VLgybGG with id 7435382
Chrome 37.0.2062 (Linux) sees changes to your_name FAILED
Expected spy Console Log to have been called with [ 'your_name is now: Bob' ] but it was never called.
at Object.<anonymous> (/home/chris/repos/polymer-book/book/code-js/changes_from_outside/test/HelloYouSpec.js:106:9)
Chrome 37.0.2062 (Linux): Executed 1 of 5 (1 FAILED) ERROR (0.065 secs / 0.061 secs)At first I assumed than an API had changed since I originally wrote this code and test (it was all the way back in March, after all). Upon closer examination, something else seems to be going wrong. I am hesitant to say that Polymer is broken, but my understanding of it is. Regardless, it sure would have been nice to have a regular build running so that I knew exactly when this did break.
First, the problem. I am spying on
console.log() which is called by a MutationObserver:function watchPolymer(el) {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(
mutation.attributeName +
' is now: ' +
mutation.target[mutation.attributeName]
);
});
});
observer.observe(el, {attributes: true});
}By virtue of the attributes: true on the observe() method, this MutationObserver should fire whenever an attribute changes. And it does, if I find the <hello-you> Polymer element and update one of its attributes, the MutationObserver fires:> el = document.querySelector('hello-you')
<hello-you color="greenish">…</hello-you>
> el.attributes.getNamedItem('color').value = 'orange'
color is now: orange watcher.js:14
"orange"But if the value of the color property changes in the element, say when the feelingLucky() button is pressed, nothing happens:<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you" attributes="your_name color" color>
<template><!-- ... --></template>
<script>
Polymer('hello-you', {
your_name: '',
color: '',
feelingLucky: function() {
console.log(this.color);
var colors = ['red', 'blue', 'green'];
var num = Math.floor(colors.length*Math.random());
this.color = colors[num];
this.$.hello.style.color = this.color;
}
});
</script>
</polymer-element>
Dang it. I am publishing the color attribute by including it in the space separated list of published attributes in the aptly named attributes attribute of the definition. But no matter what I do, changing the property is not reflected in the element's attribute.What is even stranger is that, the
console.log() on the feelingLucky() does see the initial value of the attribute. If I use this custom <hello-you> element with a “greenish” color:<hello-you color="greenish"></hello-you>Then, when I first press the
feelingLucky() button, I see greenish logged in the JavaScript console:color is now: greenish watcher.js:14 greenish hello-you.html:27It is almost like data binding is one way. That, or Polymer has some kind of internal setting that prevents published attributes from getting updated.
Wait....
I have seen this before. I know I have. Despite numerous searches and banging my head against this code for quite some time, I had no luck identifying this problem. But I know that I either
getAttribute() or setAttribute()'d before to work this exact problem. And indeed I did.I have now learned through bitter experience not once, but twice that there is special undocumented syntax that needs to be applied if one wants attributes of a Polymer element to be updated when the underlying property value is updated. I cannot set the published attributes via the
attribute attribute of polymer-element anymore. Instead I have to use the published property and set reflect to true:<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
<template><!-- ... --></template>
<script>
Polymer('hello-you', {
publish: {
your_name: {value: '', reflect: true},
color: {value: '', reflect: true},
},
// ...
});
</script>
</polymer-element>I was nearing my wits' end with this problem. That I have now been forced to solve this twice is a source of much annoyance. Hopefully the title of this post will ensure that I will have better search luck the next time that I have to do battle with this feature.Day #185
No comments:
Post a Comment