Showing posts with label e2e. Show all posts
Showing posts with label e2e. Show all posts

Wednesday, December 24, 2014

TDDing New Polymer Element Features with Protractor


Even after a week or so of working with Protractor as testing solution for Polymer, I still do not know if I have a good feel for recommending it as a solution. I begin to understand its strengths and weaknesses as a Polymer testing tool—its async support works brilliantly with Polymer while its complete lack of shadow DOM support is nearly a show stopper.

Nearly a show stopper, but when I can write tests like this:
describe('<x-pizza>', function(){
  beforeEach(function(){
    browser.get('http://localhost:8000');
  });
  it('updates value when internal state changes', function() {
    new XPizzaComponent().
      addFirstHalfTopping('pepperoni');

    expect($('x-pizza').getAttribute('value')).
      toMatch('pepperoni');
  });
});
How can I dismiss it? To be sure, the XPizzaComponent Page Object helps to mask some ugliness—but the same test in Karma has just as much shadow DOM ugliness, plus async ugliness.

I have already tried TDDing a bug fix with Protractor, which went reasonably well. So tonight I wonder what it is like TDDing a new feature? I have been playing with an early version of the <x-pizza> pizza building Polymer element as I explore these questions:



This version of the element has the exploration advantage of including some old-timey HTML form elements with which to play. The advantage here is that I made old-timey mistakes in the backing code. The bug that I TDD'd the other day resulted from adding unknown toppings to the pizza. I eliminated the bug, but it is still possible to click the “Add First Half Topping” button without choosing a topping. I would like to change that. So I write a Protractor test:
describe('<x-pizza>', function(){
  beforeEach(function(){
    browser.get('http://localhost:8000');
  });
  // ...
  it('does nothing when adding a topping w/o choosing first', function(){
    new XPizzaComponent().
      clickAddFirstHalfToppingButton();

    expect($('x-pizza').getAttribute('value')).
      toEqual('\n\n');
  });
});
I need to write the clickAddFirstHalfToppingButton() method for my page object. This is already done as part of the addFirstHalfTopping() method, but now I need it separate:
function XPizzaComponent() {
  this.selector = 'x-pizza';
}

XPizzaComponent.prototype = {
  // ...
  clickAddFirstHalfToppingButton: function() {
    browser.executeScript(function(selector){
      var el = document.querySelector(selector),
          button = el.$.firstHalf.querySelector("button");
      button.click();
    }, this.selector);
  }
};
The executeScript() method is hackery to work around Protractor's lack of shadow DOM support. It is not too horrible, but I am extremely tempted to factor the el assignment out into its own method, especially since both addFirstHalfTopping() and clickAddFirstHalfToppingButton() both assign it in the exact same way:
XPizzaComponent.prototype = {
  addFirstHalfTopping: function(topping) {
    browser.
      executeScript(function(selector, v){
        var el = document.querySelector(selector),
            select = el.$.firstHalf.querySelector("select");
      }, this.selector, topping);
  },

  clickAddFirstHalfToppingButton: function() {
    browser.
      executeScript(function(selector){
        var el = document.querySelector(selector),
            button = el.$.firstHalf.querySelector("button");
        // ...
      }, this.selector);
  }
};
The problem with trying to factor this out is that I am obtaining the element inside an anonymous function that is called by browser.executeScript() and the reason that I am doing this is because regular Protractor locators cannot find Polymer elements. The best that I can come up with is:
XPizzaComponent.prototype = {
  el: function(){
    return browser.executeScript(function(selector){
      return document.querySelector(selector);
    }, this.selector);
  },
  // ...
  clickAddFirstHalfToppingButton: function() {
    browser.
      executeScript(function(el){
        var button = el.$.firstHalf.querySelector("button");
        button.click();
      }, this.el());
  }
});
This is not too horrible, I suppose. But I am writing as much test code (and working as hard to write it) as I am with the actual code.

Regardless, I have a failing test:
$ protractor --verbose
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
<x-pizza>
  has a shadow DOM - pass
  updates value when internal state changes - pass
  syncs <input> values - pass
  does nothing when adding a topping w/o choosing first - fail

Failures:

  1) <x-pizza> does nothing when adding a topping w/o choosing first
   Message:
     Expected 'unknown

' to equal '

'.
   Stacktrace:
     Error: Failed expectation
    at [object Object].<anonymous> (/home/chris/repos/polymer-book/play/protractor/tests/XPizzaSpec.js:30:7)

Finished in 2.032 seconds
4 tests, 4 assertions, 1 failure
I can make that pass easily enough. My Polymer element's addFirstHalf() method simply needs a guard clause:
Polymer('x-pizza', {
  // ..
  addFirstHalf: function() {
    if (this.currentFirstHalf == '') return;
    this.model.firstHalfToppings.push(this.currentFirstHalf);
  },
  // ...
});
With that, I have my passing test:
$ protractor --verbose
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
<x-pizza>
  has a shadow DOM - pass
  updates value when internal state changes - pass
  syncs <input> values - pass
  does nothing when adding a topping w/o choosing first - pass

Finished in 2.012 seconds
4 tests, 4 assertions, 0 failures
And a nice new feature.

I normally do not care for refactoring tests, so the difficulty experienced tonight really should not bother me. But it does. The lack of shadow DOM support makes me think that I will need to write many helpers—if not an entire library—to make Protractor and Polymer really play nicely. If I struggle with the simple stuff like getting the Polymer element itself, this does not bode well for more in-depth testing adventures. I may play with this some more over the next few days, but I am leaning toward not using Protractor at this point.


Day #34

Tuesday, December 23, 2014

Can't Protractor Polymer on Internet Explorer


All right, let's see if I can figure out what went wrong while running my Polymer + Protractor tests in Internet Explorer. On the face of it, Polymer and Protractor are not an obvious combination since Polymer is a web component (not an application) and Protractor is designed to test single-page applications, specifically AngularJS. Also, WebDriver (on which Protractor is based) does not support shadow DOM. That said, there are some definite reasons to like the Polymer + Protractor combination. Plus, tests should work on all browsers, including Internet Explorer.

Emphasis on should.

I am unsure where I went wrong the other night, but trying to run this stuff on IE11 instead of 10 seemed a next logical step. So I retrace my steps from that post (and from the source post). On the windows virtual machine, I install node.js. I install Java. I open a command prompt and install protractor globally (npm install -g protractor).

Side note, I omitted installing the IE WebDriver in the previous post. When I try to start WebDriver without it, I see:
C:\Users\IEUser>webdriver-manager start --seleniumPort=4411
Selenium Standalone is not present. Install with webdriver-manager update --standalone
The fix is to tell the webdriver manager to install the IE driver:
C:\Users\IEUser>webdriver-manager update --ie
With that, I can start my WebDriver server:
C:\Users\IEUser>webdriver-manager start --seleniumPort=4411
20:27:18.602 INFO - Launching a standalone server
Setting system property webdriver.ie.driver to C:\Users\IEUser\AppData\Roaming\npm\node_modules\protractor\selenium\IEDriverServer.exe
20:27:18.894 INFO - Java: Oracle Corporation 25.25-b02
20:27:18.908 INFO - OS: Windows 7 6.1 x86
20:27:18.922 INFO - v2.44.0, with Core v2.44.0. Built from revision 76d78cf
20:27:19.145 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4411/wd/hub
...
20:27:19.353 INFO - Started SocketListener on 0.0.0.0:4411
Back on my Linux machine, I update the protractor.conf.js to point to this IE11 instance of WebDriver:
exports.config = {
  seleniumAddress: 'http://localhost:4411/wd/hub',
  capabilities: {
    'browserName': 'internet explorer',
    'platform': 'ANY',
    'version': '11'
  },
  // seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['tests/XPizzaSpec.js'],
  onPrepare: function() {
    require('./tests/polymerBrowser.js');
    require('./tests/xPizzaComponent.js');
  }
};
And, I also need to point the specs to my IP address instead of localhost so that the IE VM can connect to it:
describe('', function(){
  beforeEach(function(){
    browser.get('http://192.168.1.129:8000');
    // browser.get('http://localhost:8000');
  });
  // Tests here...
});
Last, I add a port forwarding rule on the VM so that my Protractor tests on the Linux box can connect to that WebDriver address on post 4411 of localhost.

With that… I see the exact same behavior that I saw the other night with IE10:
$ protractor --verbose
Using the selenium server at http://localhost:4411/wd/hub
[launcher] Running 1 instances of WebDriver
<x-pizza>
  has a shadow DOM - pass
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was:
WebDriver.findElements(By.cssSelector("x-pizza"))
    at [object Object].webdriver.WebDriver.schedule (/home/chris/local/node-v0.10.20/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/webdriver/webdriver.js:345:15)
    at [object Object].webdriver.WebDriver.findElements (/home/chris/local/node-v0.10.20/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/webdriver/webdriver.js:934:17)
  ...
  updates value when internal state changes - fail
Sigh.

As I saw with IE, the first test, which asks the Polymer element to send back its shadow DOM innerHTML via a Protractor executeScript(), works. The second test, which adds pepperoni toppings to the <x-pizza> element then asserts on the value attribute, fails. What is weird about the failure is that I can see the pepperoni toppings added in WebDriver IE:



So the problem is not interacting with the Polymer element, it is querying the value property. Interacting with the element involved a bit of hackery since Protractor does not support the shadow DOM, but the hackery would seem to work. What does not work is the thing that should work without any hackery at all—querying element attributes.

Of course, the hackery could be partially to blame. This works in Chrome and Firefox, but that does not necessarily work in all browsers. To see if that is the case, I remove the page object that updates the pizza toppings:
describe('<x-pizza>', function(){
  beforeEach(function(){
    browser.get('http://192.168.1.129:8000');
    // browser.get('http://localhost:8000');
  });

  it('updates value when internal state changes', function() {
    // new XPizzaComponent().
    //   addFirstHalfTopping('pepperoni');

    expect($('x-pizza').getAttribute('value')).
      toMatch('pepperoni');
  });
});
The test should fail, but hopefully it will be an assertion failure, not a timeout.

Unfortunately, it has no effect.

I am at a loss at what to try next. The developer tools on my IE11 installation are useless (I mean more than normal—I cannot type in the console). I would like to be able to test against IE, but it is not that important to me. I am inclined to set this aside until inspiration (or a driver update) arrive. Until then, I will likely move onto to other topics.


Day #33

Monday, December 22, 2014

Fixing Polymer Bugs with Protractor


Today, I hope to get a feel for fixing bugs in Polymer elements by driving the fixes with Protractor.

A while back, I noticed a problem in my <x-pizza> pizza building element:



The problem occurs when adding non-standard toppings, which results in a JavaScript error. I actually noticed the problem while writing Protractor tests, so if I can fix it with another Protractor test, it would make for nice symmetry.

My Protractor test for adding valid toppings looks like:
describe('<x-pizza>', function(){
  beforeEach(function(){
    browser.get('http://localhost:8000');
  });
  it('updates value when internal state changes', function() {
    new XPizzaComponent().
      addFirstHalfTopping('pepperoni');

    expect($('x-pizza').getAttribute('value')).
      toMatch('pepperoni');
  });
});
That is nice and compact thanks to the Page Objects pattern—the XPizzaComponent in this case.

The page object makes it easy to write a new test that breaks things:
  it('adds "unknown" topping when invalid item selected', function() {
    new XPizzaComponent().
      addFirstHalfTopping('');

    expect($('x-pizza').getAttribute('value')).
      toMatch('unknown');
  });
Instead of choosing an option from the list, this should select the first item on the drop down list, which is blank. When I run the Protractor test, I get a failure, but not an error:
  1) <x-pizza> adds "unknown" topping when invalid item selected
   Message:
     Expected '

' to match 'unknown'.
   Stacktrace:
     Error: Failed expectation
    at [object Object].<anonymous> (/home/chris/repos/polymer-book/play/protractor/tests/XPizzaSpec.js:31:7)
If I add a browser.pause():
  it('adds "unknown" topping when invalid item selected', function() {
    new XPizzaComponent().
      addFirstHalfTopping('');

    browser.pause();

    expect($('x-pizza').getAttribute('value')).
      toMatch('unknown');
  });
Then I can see the error in the WebDriver console:



I find that I can also use this very verbose code to print out the console:
  it('adds "unknown" topping when invalid item selected', function() {
    new XPizzaComponent().
      addFirstHalfTopping('');

    browser.manage().logs().get('browser').then(function(browserLog) {
      console.log('log: ' + require('util').inspect(browserLog));
    });

    expect($('x-pizza').getAttribute('value')).
      toMatch('unknown');
  });
Regardless of how I find the error, it is fairly easy to fix it. The console in both cases reports the error to be at:
ReferenceError: _svgUnknown is not defined
    at x-pizza.Polymer._toppingMaker (http://localhost:8000/elements/x_pizza.js:94:12)
Looking at the _toppingMaker() method, I see right away that, unlike the SVG makers for know elements, I am trying to call _svgUnknown() as a function instead of a method:
  _toppingMaker: function(topping) {
    if (topping == 'pepperoni') return this._svgPepperoni;
    if (topping == 'sausage') return this._svgSausage;
    if (topping == 'green peppers') return this._svgGreenPepper;
    return _svgUnknown;
  },
And, indeed, converting that to a method resolves the bug:
  _toppingMaker: function(topping) {
    if (topping == 'pepperoni') return this._svgPepperoni;
    if (topping == 'sausage') return this._svgSausage;
    if (topping == 'green peppers') return this._svgGreenPepper;
    return this._svgUnknown;
  },
Just as importantly, I know have an e2e test that exercises this code, ensuring that I do not make a dumb mistake again:
<x-pizza>
  has a shadow DOM - pass
  updates value when internal state changes - pass
  syncs <input> values - pass
  adds "unknown" topping when invalid item selected - pass


Finished in 1.938 seconds
4 tests, 4 assertions, 0 failures
This might seem like a small thing for all that test code, but if I made that mistake once, I can make it again. Unless I have a test.


Day #32


Sunday, December 21, 2014

Protractor, Polymer, and Other Browsers


I am quickly warming to the idea of using Protractor for testing my Polymer elements. As of last night, I have some very nice tests.

They work well in Chrome:
$ protractor --verbose --browser=chrome
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
<x-pizza>
  has a shadow DOM - pass
  updates value when internal state changes - pass
  syncs <input> values - pass


Finished in 1.397 seconds
3 tests, 3 assertions, 0 failures

[launcher] 0 instance(s) of WebDriver still running
[launcher] chrome #1 passed
They even run well in Firefox:
$ protractor --verbose --browser=firefox
Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
<x-pizza>
  has a shadow DOM - pass
  updates value when internal state changes - pass
  syncs <input> values - pass


Finished in 2.183 seconds
3 tests, 3 assertions, 0 failures

[launcher] 0 instance(s) of WebDriver still running
[launcher] firefox #1 passed
I will probably regret this, but what about Internet Explorer?

There is a nicely done post on getting Protractor working on Windows, but there are a crazy number of steps in there. I opt for the post's alternative #1 and install via Node.js. I do this in my VirtualBox VM for IE10.

I install Node the usual way then run npm install -g protractor to install it and the webdriver-manager globally:



When I try to run webdriver-manager start --seleniumPort=4410 however, I find that I do not have Java installed:



So it seems that I cannot skip all of the steps from that post. No matter, I install Java the usual way. I accept the defaults and enable any networking for which I am prompted:



With that, I have WebDriver running in my Windows VM.

Back in my Linux system that runs the actual tests, I change the configuration to point to the Windows webdriver instance:
exports.config = {
  //seleniumAddress: 'http://localhost:4444/wd/hub',
  seleniumAddress: 'http://localhost:4410/wd/hub',
  capabilities: {
    'browserName': 'internet explorer',
    'platform': 'ANY',
    'version': '10'
  },
  specs: ['tests/XPizzaSpec.js'],
  onPrepare: function() {
    require('./tests/polymerBrowser.js');
    require('./tests/xPizzaComponent.js');
  }
}
Ah, but that will not work without port forward. In the network setting of my VM, I click the port forwarding button:



In the resulting dialog, I tell VirtualBox to forward requests to my Linux box's port 4410 to the same TCP port in my Windows VM:



Even that does not quite work as my Windows VM cannot access the server that hosts my Polymer element. Bleh.

I work around this by rewriting my test to request the hostname of my Linux machine (instead of localhost):
describe('<x-pizza>', function(){
  beforeEach(function(){
    // browser.get('http://localhost:8000');
    browser.get('http://serenity.local:8000');
  });
  // Tests here...
});
That gets Protractor on my Linux box talking successfully to WebDriver on the Windows VM. I can even see it interacting with my <x-pizza> Polymer element:



But try as I might, I cannot get it to query the document for values against which to run my assertions:
$ protractor --verbose
Using the selenium server at http://localhost:4410/wd/hub
[launcher] Running 1 instances of WebDriver
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was:
WebDriver.findElements(By.cssSelector("input[type=hidden]"))
    at [object Object].webdriver.WebDriver.schedule (/home/chris/local/node-v0.10.20/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/w
ebdriver/webdriver.js:345:15)
    at [object Object].webdriver.WebDriver.findElements (/home/chris/local/node-v0.10.20/lib/node_modules/protractor/node_modules/selenium-webdriver/l
ib/webdriver/webdriver.js:934:17)
...
<x-pizza>
  syncs <input> values - fail


Failures:

  1) <x-pizza> syncs <input> values
   Message:
     timeout: timed out after 30000 msec waiting for spec to complete
   Stacktrace:
     undefined

Finished in 32.849 seconds
1 test, 1 assertion, 1 failure
I cannot figure out why this is timing out. I can run assertions against some text values, but cannot get properties of elements on the page—Polymer or regular elements. While the test is timing out, I can even pop open the Developer Tools' console on IE and verify that it can query the value for which it is testing:



But, for some as yet unknown reason, I cannot get my local Protractor to successfully run this same query.

I really hate debugging code on IE so I may just punt on this. Or I could try a simpler test case to attempt to isolate the problem. Or maybe, if I am feeling industrious tomorrow, I will install all of this on a VM with IE11 instead of 10. But I'd have to be feeling pretty industrious to go through all of this again.


Day #31

Friday, December 19, 2014

On Polymer, Protractor, and Page Objects


Tonight, I continue to abuse Protractor, forcing it to perform end-to-end (e2e) tests against Polymer instead of its preferred AngularJS. Protractor provides some facilities for testing non-Angular applications, but is hampered in testing Polymer elements since the underlying WebDriver does not support the Shadow DOM on which Polymer relies heavily. Even so, it has already provided useful insights into my Polymer element—and made for some pretty tests.

The Polymer element that is being tested remains an early version of the <x-pizza> pizza builder used extensively in Patterns in Polymer:



Last night's test veered a bit away from pretty tests, mostly thanks to a call to Protractor's browser.executeScript(), which I use to workaround WebDriver's lack of shadow DOM support. Said script comes in the form of a brutal string:
describe('<x-pizza>', function(){
  beforeEach(function(){
    browser.get('http://localhost:8000');
    expect($('[unresolved]').waitAbsent()).toBeTruthy();
  });
  it('updates value when internal state changes', function() {
    var selector = 'x-pizza';
    browser.executeScript(
      'var el = document.querySelector("' + selector + '"); ' +
      'var options = el.shadowRoot.querySelectorAll("#firstHalf option"); ' +
      'options[1].selected = true; ' +
      '' +
      'var e = document.createEvent("Event"); ' +
      'e.initEvent("change", true, true); ' +
      'options[1].dispatchEvent(e); ' +
      '' +
      'button = el.shadowRoot.querySelector("#firstHalf button"); ' +
      'button.click(); '
    );
    expect($(selector).getAttribute('value')).
      toMatch('pepperoni');
  });
  // ...
});
The code in there is ugly enough to begin with, but why-oh-why does Protractor force me to build strings like that?!

Er… it doesn't.

It turns out that I am as guilty as anyone else in finding a solution when scrambling to meet a deadline. And I am just as guilty here in never bothering to check the documentation which is perfectly clear. At any rate, I can clean that code up a bit with:
  it('updates value when internal state changes', function() {
    var selector = 'x-pizza';

    browser.executeScript(function(selector){
      var el = document.querySelector(selector);
      var options = el.shadowRoot.querySelectorAll("#firstHalf option");
      options[1].selected = true;

      var e = document.createEvent("Event");
      e.initEvent("change", true, true);
      options[1].dispatchEvent(e);

      button = el.shadowRoot.querySelector("#firstHalf button");
      button.click();
    }, [selector]);

    expect($(selector).getAttribute('value')).
      toMatch('pepperoni');
  });
That is still not pretty, but the ugly is down by an order of magnitude. It is a little annoying supplying the "x-pizza" selector in the list of arguments at the end of the executeScript(), but the real ugly remains in the form of that createEvent() inside the executeScript() that triggers a Polymer-required event.

It sure would be nice if I could click() the <select>, then <click> the correct <option>. Alas, this is WebDriver shadow DOM limitation in action. If I grab the <select> from executeScript(), then try to click it:
  it('updates value when internal state changes', function() {
    var selector = 'x-pizza';
    var toppings = browser.executeScript(function(selector){
      var el = document.querySelector(selector);
      return el.shadowRoot.querySelector('#firstHalf select');
    }, [selector]);

    toppings.then(function(toppings){
      toppings.click();
    });
    // ...
  });
I get ye olde stale element error:
1) <x-pizza> updates value when internal state changes
   Message:
     StaleElementReferenceError: stale element reference: element is not attached to the page document
  (Session info: chrome=39.0.2171.95)
  (Driver info: chromedriver=2.12.301324 (de8ab311bc9374d0ade71f7c167bad61848c7c48),platform=Linux 3.13.0-37-generic x86_64) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 10 milliseconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.44.0', revision: '76d78cf', time: '2014-10-23 20:02:37'
System info: host: 'serenity', ip: '127.0.0.1', os.name: 'Linux', os.arch: 'amd64', os.version: '3.13.0-37-generic', java.version: '1.7.0_65'
Session ID: c91503b6a80a9931478f6908f80f16d4
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=LINUX, acceptSslCerts=true, javascriptEnabled=true, browserName=chrome, chrome={userDataDir=/tmp/.com.google.Chrome.Dfu9DJ}, rotatable=false, locationContextEnabled=true, mobileEmulationEnabled=false, version=39.0.2171.95, takesHeapSnapshot=true, cssSelectorsEnabled=true, databaseEnabled=false, handlesAlerts=true, browserConnectionEnabled=false, webStorageEnabled=true, nativeEvents=true, applicationCacheEnabled=false, takesScreenshot=true}]
The usual state element fixes have no effect since this element is still attached—but attached in the unreachable (for WebDriver) shadow DOM.

So I am stuck with the ugly. Or am I?

Page Objects to the rescue:
  it('updates value when internal state changes', function() {
    new XPizzaComponent().
      addFirstHalfTopping('pepperoni');

    expect($('x-pizza').getAttribute('value')).
      toMatch('pepperoni');
  });
To make that work, I need to define the object in the Protractor onPrepare(), which resides in the configuration file. First, I need to add the object to Protractor's global namespace:
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['tests/XPizzaSpec.js'],
  onPrepare: function() {
    browser.ignoreSynchronization = true;

    global.XPizzaComponent = XPizzaComponent;
    function XPizzaComponent() {
      this.selector = 'x-pizza';
    }
  }
};
Then I only need to define the addFirstHalfTopping() method, which mostly comes from the "ugly" test code:
    XPizzaComponent.prototype = {
      addFirstHalfTopping: function(topping) {
        browser.executeScript(function(selector, v){
          var el = document.querySelector(selector);

          var select = el.$.firstHalf.querySelector("select"),
              button = el.$.firstHalf.querySelector("button");

          var index = -1;
          for (var i=0; i<select.length; i++) {
            if (select.options[i].value == v) index = i;
          }
          select.selectedIndex = index;

          var event = document.createEvent('Event');
          event.initEvent('change', true, true);
          select.dispatchEvent(event);

          button.click();
        }, this.selector, topping);
      }
    };
Since I "hide" this ugly in a page object, I have less compunction over using Polymer properties or methods. In this case, I use the dollar sign property to get the container element with the ID of firstHalf.

In the end, this is very similar to the page object work that I did in Karma. Only now I can rely on Protractor's promises to wait for the element's value property to be present instead of the async() callback craziness that was required for Karma. That is an exciting win.

Now the ugly, at least what is left of it, all resides in the Page Object defined in the configuration. I can definitely live with that. More than that—I may be starting to like Protractor as a Polymer testing solution.


Day #29