How can I pass a wct test while rearranging children spans in a Polymer element?

287 views Asked by At

I am creating a polymer element called "price-text" that displays a monetary value and some text associated with it. I am also adding the ability to reverse their positions when the "swap" attribute is present. I have performed a large amount of testing on this element using wct with no issue until I set up a fixture using the swap attribute, despite being able to successfully use the swap attribute in a browser demo to reverse the order in which the spans (inside "price-text") are displayed.

I can only observe the error when wct opens the browsers to run the test. This is the error I see in the browser:

Error: the string "console.error:Error stamping [object HTMLUnknownElement] TypeError: Unable to get property 'parentNode' of undefined or null reference" was thrown, throw an Error :)

After wct completes all of the suties it will still return "Tests Passed" to the terminal.

I have determined that the the setup function will run, and the error occurs as soon as the test function is called. To restate my goal, I would like to be able to use wct to pass the two simple "expect" tests in this suite where spans change positions.

Here is the test fixture.

  <test-fixture id="price-text-swapd">
    <template>
      <price-text price="6.99" text="meal" swap></price-text>
    </template>
  </test-fixture> 

Here is the script associated with this test fixture.

  suite('price and text are swapd', function() {

    var el;
    var priceTextLineEl;

    setup(function() {
      el = fixture('price-text-swapd');
      priceTextLineEl = Polymer.dom(el.root).querySelector("#price-text-line");
    });

    test('defines the swap property', function(done) {
      flush(function() {
        expect(el.swap).to.be.a('boolean');
        expect(el.swap).to.equal(true);
        done();
      });
    });
  });

Here is the price-text element.

  <span id="price-text-line">
    <span id="price" class="left border">{{_formattedPrice}}</span><span id="text" class="right">{{_formattedText}}</span>
  </span>

Here is the function controlling the spans in the shadow DOM

  _swapPriceText: function() {
    var textEl = document.getElementById("text");
    var priceEl = document.getElementById("price")
    if (this.swap) {
      textEl.parentNode.insertBefore(textEl, textEl.parentNode.firstChild);
      textEl.classList.add("left");
      textEl.classList.remove("right");
      priceEl.classList.add("right");
      priceEl.classList.remove("left");
    } 
  }

I've posted the test and element file in their entirety in the following snippets.

<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<link rel="import" href="../polymer/polymer.html">

<!--
Variable price and text sparated by a border.

Example:

    <price-text price="6.99" text="meal"></price-text>

@demo demo/index.html
@hero hero.svg
-->

<dom-module id="price-text">
  <template>
    <style>
      :root {
        --font-family: 'Helvetica', arial;
        --font-size: 32px;
        --text-font-size: 21px;
        --price-padding-left: 0;
        --price-padding-right: 4px;
        --price-border-right: 1px solid #000;
        --price-border-left: 1px solid #000;
        --text-padding-left: 8px;
        --text-padding-right: 0;
      }

      :host {
        display: inline-block;
        box-sizing: border-box;
      }

      #price-text-line {
        font-family: var(--font-family);
        @apply(--price-text-line);
      }

      #price {
        display: inline-block;
        font-size: var(--font-size);
        @apply(--price-text-line--price);
      }

      #text {
        display: inline-block;
        font-size: var(--text-font-size);
        @apply(--price-text-line--text);
      }

      .left {
        padding-left: var(--price-padding-left);
        padding-right: var(--price-padding-right);;
        @apply(--price-text-line--left);
      }

      .right {
        padding-left: var(--text-padding-left);
        padding-right: var(--text-padding-right);
        @apply(--price-text-line--right);
      }

      .border.right {
        border-left: var(--price-border-right);
      }

      .border.left {
        border-right: var(--price-border-left);
      }


   
    </style>

    <span id="price-text-line">
      <span id="price" class="left border">{{_formattedPrice}}</span><span id="text" class="right">{{_formattedText}}</span>
    </span>
  </template>

  <script>
    Polymer({
      is: 'price-text',

      properties: {
        /**
         * `price` accepts input in the form of numbers and passes the data to the `formatCurrency()` method.
         *
         * @type {{price: Number}}
         */
        price: {
          type: Number,
          value: 0,
          observer: '_formatCurrency'
        },
        /**
         * `formattedPrice` accepts formatted data from the `formatCurrency()` method to be displayed in .price.
         *
         * @type {{formattedPrice: String}}
         */
        _formattedPrice: {
          type: String,
          value: ""
        },
        /**
         * `text` accepts input in the form of strings and passes the data to the `formatText()` method.
         *
         * @type {{text: String}}
         */
        text: {
          type: String,
          value: "",
          observer: '_formatText'
        },
        /**
         * formattedText accepts formatted data from the `formatText()` method to be displayed in .text.
         *
         * @type {{formattedText: String}}
         */
        _formattedText: {
          type: String,
          value: ""
        },

        swap: {
          type: Boolean,
          observer: '_swapPriceText'
        }

      },

      /**
       * `formatCurrency()` accepts data from `price`, checks to see if it is a number greater than zero, rounds the number to two decimal places, and passes the data to `formattedPrice`.  If the data is not a number it will set `formattedCurrency` to an empty string.
       *
       */
      _formatCurrency: function() {
        if (this.price > 0) {
          this._formattedPrice = '$' + (this.price).toFixed(2);
        } else {
          this._formattedPrice = '';
        }
      },
      /**
       *`formatText()` accepts data from `text`, checks to see if it is not a number, and passes the data to `formattedText`.  If the data is a number, it will set `formattedText` to an empty string.
       *
       */
      _formatText: function() {
        if (isNaN(this.text)) {
          this._formattedText = this.text;
        } else {
          this._formattedText = '';
        }
      },

      /**
       *`swapPriceText` swaps the position of the spans containing the `price` and `text` data.
       *
       */

      _swapPriceText: function() {
        var textEl = document.getElementById("text");
        var priceEl = document.getElementById("price")
        if (this.swap) {
          textEl.parentNode.insertBefore(textEl, textEl.parentNode.firstChild);
          textEl.classList.add("left");
          textEl.classList.remove("right");
          priceEl.classList.add("right");
          priceEl.classList.remove("left");
        } else {
          priceEl.parentNode.insertBefore(priceEl, priceEl.parentNode.firstChild);
          textEl.classList.add("right");
          textEl.classList.remove("left");
          priceEl.classList.add("left");
          priceEl.classList.remove("right");

        }

      }





    });
  </script>
</dom-module>

<!doctype html>
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">

    <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
    <script src="../../web-component-tester/browser.js"></script>

    <!-- Step 1: import the element to test -->
    <link rel="import" href="../price-text.html">
  </head>
  <body>

    <!-- You can use the document as a place to set up your fixtures. -->
    <test-fixture id="has-price-and-text">
      <template>
        <price-text price="6.99" text="meals"></price-text>
      </template>
    </test-fixture>
    <test-fixture id="has-price-not-text">
      <template>
        <price-text price="42.42"></price-text>
      </template>
    </test-fixture>
    <test-fixture id="has-text-not-price">
      <template>
        <price-text text="sandwich"></price-text>
      </template>
    </test-fixture>
    <test-fixture id="has-wrong-price-data">
      <template>
        <price-text price="shake" text="fries"></price-text>
      </template>
    </test-fixture>
    <test-fixture id="has-wrong-text-data">
      <template>
        <price-text price="42.00" text="0.42"></price-text>
      </template>
    </test-fixture>
    <test-fixture id="price-text-undefined">
      <template>
        <price-text></price-text>
      </template>
    </test-fixture>
    <test-fixture id="price-text-swapd">
      <template>
        <price-text price="6.99" text="meal" swap></price-text>
      </template>
    </test-fixture>

    <script>
      suite('define price and text', function() {
      
        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('has-price-and-text');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text')
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(el.price).to.equal(6.99);
        });

        test('defines the text properly', function() {
          expect(el.text).to.be.a('string');
          expect(el.text).to.equal('meals');
        });

        test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('$6.99');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('meals')
        });

      });

      suite('define price but not text', function() {

        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('has-price-not-text');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text');
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(el.price).to.equal(42.42);
        });

        test('defines the text property', function() {
          expect(el.text).to.be.a('string');
          expect(el.text).to.equal('');
        });

        test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('$42.42');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('')
        });

      });

      suite('price data is wrong', function() {

        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('has-wrong-price-data');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text');
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(isNaN(el.price)).to.be.ok;
        });

        test('defines the text property', function() {
          expect(el.text).to.be.a('string');
          expect(el.text).to.equal('fries');
        });

        test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('fries')
        });

      });

      suite('define text but not price', function() {

        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('has-text-not-price');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text');
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(el.price).to.equal(0);
        });

        test('defines the text property', function() {
          expect(el.text).to.be.a('string');
          expect(el.text).to.equal('sandwich');
        });

        test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('sandwich')
        });

      });

      suite('text data is wrong', function() {

        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('has-wrong-text-data');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text');
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(el.price).to.equal(42.00);
        });

        test('defines the text property', function() {
          expect(el.text).to.be.a('string');
          expect(!isNaN(el.text)).to.be.ok;
        });

        test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('$42.00');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('')
        });

      });

      suite('price and text are not defined', function() {

        var el;
        var priceEl;
        var textEl;

        setup(function() {
          el = fixture('price-text-undefined');
          priceEl = Polymer.dom(el.root).querySelector('#price');
          textEl = Polymer.dom(el.root).querySelector('#text');
        });

        test('defines the price property', function() {
          expect(el.price).to.be.a('number');
          expect(el.price).to.equal(0);
        });

        test('defines the text property', function() {
          expect(el.text).to.be.a('string');
          expect(el.text).to.equal('');
        });

         test('is price displaying properly', function() {
          expect(priceEl.innerHTML).to.equal('');
        });

        test('is text displaying properly', function() {
          expect(textEl.innerHTML).to.equal('')
        });

      });

      suite('price and text are swapd', function() {
        
        var el;
        var priceTextLineEl;

        setup(function() {
          el = fixture('price-text-swapd');
          priceTextLineEl = Polymer.dom(el.root).querySelector("#price-text-line");
        });
/*
        test('defines the swap, price, and text properties', function(done) {
          flush(function() {
            expect(el.swap).to.be.a('boolean');
            expect(el.swap).to.equal(true);
            expect(el.price).to.be.a('number');
            expect(el.price).to.equal(6.99);
            expect(el.text).to.be.a('string');
            expect(el.text).to.equal('meal');
            done();
          });
        });*/

/*        test('is swap displaying properly', function() {
          expect(priceTextLineEl.innerHTML).to.equal('<span id="text" class="right">meal</span><span id="price" class="left border">meal</span>');
        });
*/
      });
    </script>

  </body>
</html>

1

There are 1 answers

1
a1626 On BEST ANSWER

Use this.$.text and this.$.price in _swapPriceText method. This will solve your issue. Always prefer to use above mentioned method or this.$$('#id') in case of dom-repeat in Polymer to get nodes. It is recommended method by polymer.

Also you don't need flush method that is needed in case of dom-repeat