Saturday, December 13, 2014

Can't Even Polymer with Intern and Local Selenium Server


I hate a problem unsolved. I really ought to move onto more useful ground, but I cannot for the life of me understand why I cannot get The Intern tester running with Polymer elements. So tonight, I try to remove one potential problem point from the equation: Sauce Labs.

Really, there is no reason that this should work, but if nothing else, running locally will give me more real time feedback on what might possibly be going on here. But I'll be honest here, I hate Selenium locally (which is one of the reasons I really appreciate Sauce Labs).

Running it is easy enough. I download the latest standalone server from the download page. I can then run the server immediately with:
$ java -jar Downloads/selenium-server-standalone-2.44.0.jar
00:07:06.282 INFO - Launching a standalone server
00:07:06.366 INFO - Java: Oracle Corporation 24.65-b04
00:07:06.366 INFO - OS: Linux 3.13.0-37-generic amd64
00:07:06.388 INFO - v2.44.0, with Core v2.44.0. Built from revision 76d78cf
...
00:07:06.606 INFO - Started HttpContext[/wd,/wd]
00:07:06.611 INFO - Started SocketListener on 0.0.0.0:4444
But for the life of me, I cannot figure out how to install the Firefox driver. How stupid is that? This must be so obvious to just about everyone else, but I've got nothing.

Eventually, I read that the extension is contained in the standalone server. I would think that would mean that I could install it from the running server, but if that is case, I am unable to make it happen. So I unzip the JAR file:
$ unzip ../selenium-server-standalone-2.44.0.jar
...
   creating: org/openqa/grid/selenium/
   creating: org/openqa/grid/selenium/proxy/
   creating: org/openqa/grid/selenium/utils/
  inflating: org/openqa/grid/selenium/GridLauncher.class
  inflating: org/openqa/grid/selenium/proxy/DefaultRemoteProxy$1.class
  inflating: org/openqa/grid/selenium/proxy/DefaultRemoteProxy.class
  inflating: org/openqa/grid/selenium/utils/WebProxyHtmlRenderer.class
That does contain the XPI:
$ find | grep xpi$
./org/openqa/selenium/firefox/webdriver.xpi
Armed with that, I install it by directly pointing Firefox to the location on disk:



After allowing this to occur:



I am ready to make some changes to the Intern tests/intern.js configuration code. I switch from a specific browser version on Sauce Labs to my local Firefox:
  environments: [
  // { browserName: 'chrome', version: '34', platform: [ 'Linux' ] }
  { browserName: 'firefox' }
  ]
I switch from the Sauce Labs tunnel to no tunnel:
  // Name of the tunnel class to use for WebDriver tests
  //tunnel: 'SauceLabsTunnel',
  tunnel: 'NullTunnel',
  useSauceConnect: false,
Finally, I add webdriver configuration (this may not be necessary):
  webdriver: {
    host: 'localhost',
    port: 4444
  },
With that in place, I am ready to try again… which results in:
$ ./node_modules/.bin/intern-runner config=tests/intern
Listening on 0.0.0.0:9000
Starting tunnel...
Initialised firefox 34.0 on LINUX
Test main - index - <x-pizza> FAILED on firefox 34.0 on LINUX:
CancelError: Timeout reached on main - index - <x-pizza>
Error: Timeout reached on main - index - <x-pizza>
  at Error  <anonymous>
  at new ErrorCtor  <node_modules/intern/node_modules/dojo/errors/create.js:13:21>
  at null._onTimeout  <node_modules/intern/lib/Test.js:180:38>
  at Timer.listOnTimeout [as ontimeout]  <timers.js:110:15>
Unfortunately, this seems even worse than yesterday. The timeout is occurring while waiting for the Polymer element's shadow root property to come into existence:
    '<x-pizza>': function () {
      return this.remote
        .get(require.toUrl('../index.html'))
        .setFindTimeout(5000)
        .findByXpath('//body[not(@unresolved)]')
        .then(pollUntil('return document.querySelector("x-pizza").shadowRoot;', 1000))
        .findByTagName('x-pizza')
        // .execute('return document.querySelector("x-pizza");')
        .then(function (el) {
           console.log(el);
           console.log(el.$);
           console.log(el.constructor);
           console.log(el.shadowRoot);
           console.log(el[0]);
           assert.isDefined(el.shadowRoot);
         });
    }
That at least happened with Sauce Labs, but never returns locally. This is baffling because I can see the <x-pizza> element just starting to be resolved after Polymer has initialized and removed the unresolved attribute from the <body>:



Unfortunately, I cannot think of anything else to try tonight that I had not already tried last night with Sauce Labs. If nothing else, I can see very definitely tonight that the <x-pizza> element is present and should have a shadowRoot (which is where the SVG pizza resides). But for whatever reason, the Intern is unable to access this element for testing.

I think I am ready to give up on Intern for real now, but I may give Protractor a whirl with this tomorrow. Even if this particular WebDriver tool cannot yet test Polymer, perhaps another tool can.


Day #23


8 comments:

  1. Hello Chris,

    Thanks for the great write up about Intern and Polymer. It helped me get up and running with Intern to test Polymer.

    In your case it should be related to the fact that custom elements do not have 'shadowRoot' in FireFox as it has not yet been implemented in FireFox. There are some Polyfills to do the job. So even manually in the console the following would return `false`:

    ```
    var pizza_elements = document.getElementsByTagName('x-pizza');
    pizza_elements[0].hasOwnProperty('shadoRoot'); // Returns false in FireFox, return true in Chrome

    ```

    Remove FireFox from the environments and see how it works. Then you would be able to adopt the tests accordingly.

    Thanks for the great blog posts, they have been very helpful. Keep up the great work.

    ReplyDelete
    Replies
    1. Even in Firefox, the Polymer element has a shadowRoot property (even if it's not a hasOwnProperty). The test is accessing it as a regular property, so I don't know why it would behave differently in Chrome. Still, I'll be sure to give it a try.

      Glad you were able to get it working!

      Delete
    2. Thanks Chris. I came up with some thing which works but it is pretty ugly.

      You have to return whatever you want to test in the `pollUntil` function. It wraps it in a Promise and in the `then` part you could grab the value and do the assert.
      The sad part is returning the element itself won't work as you will get the same:

      stale element reference: element is not attached to the page document

      So in case of your code it would be something like this:

      '': function () {
      return this.remote
      .get(require.toUrl('../index.html'))
      .setFindTimeout(5000)
      .findByXpath('//body[not(@unresolved)]')
      .then(pollUntil(function () {
      if (document.querySelector("x-pizza").shadowRoot) {
      return document.querySelector("x-pizza").shadowRoot.querySelector("nested-x-pizza").id;
      }
      return null;
      }, [], 1000))
      .then(function (pizzaId) {
      assert.equal(pizzaId, 'expectedValue');
      }, function (err) {

      });
      }

      Delete
    3. Yup, that sounds similar to what I wound up doing in Protractor (another webdriver testing tool): http://japhr.blogspot.com/2014/12/protractor-can-test-polymer-sort-of.html.

      I begin to think that webdriver-based tools are not appropriate for testing Polymer, mostly because of the inability to access the shadow dom (but for other reasons as well).

      Delete
  2. Web Component Tester uses Polymer.whenReady() to wait till polymer has done all its microtasks. May be you can try it out. I have internjs on my to learn list still –

    ReplyDelete
    Replies
    1. Yup, I use Polymer.whenReady() in the book for testing as well. Unfortunately, webdriver does not grant access to the Polymer top-level object (as best I can tell). Without it, that doesn't work :(

      Delete
  3. Thanks Chris.

    For future references I summarized my experience in a blog post: http://mmxgroup.net/2014/12/16/testing-a-google-polymer-project-using-internjs/

    ReplyDelete
    Replies
    1. Cool! Saves me the trouble of having to figure it out and write it up myself. Thanks :)

      Delete