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