1

Here is my property declaration:

  properties: {

    size: {
      type: String,
      value: ''
    }, 

    sizeName: {
      type: String,
      value: ''
    }
  }

My CSS:

<style>
  :root {
    --width: 20px;
    --height: 20px;
    --border-color: black;
    --font-size: 16px;
  }
  :host {
    display: inline-block;
    box-sizing: border-box;
    height: var(--height);
    width: var(--width);
  }
  #icon {
    position: relative;
    display: block;
    width: var(--width);
    height: var(--height);
    border-radius: 50%;
    border: 1px solid var(--border-color);
    font-size: var(--font-size);
    @apply(--size-icon);
  }
  #icon:before {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 100%;
    content: '?';
    text-align: center;
    @apply(--size-icon--before);
  }
  #icon.small:before {content: 's';}
  #icon.medium:before {content: 'm';}
  #icon.large:before {content: 'l';}

  #name {
    font-size: var(--font-size);
    @apply(--size-name);
  }

  .hidden {
    display: none;
  }
</style>

I have a test fixture as follows:

<test-fixture id="hasWrongSize">
  <template>
    <size-icon size="asdf"></size-icon>
  </template>
  <template>
    <size-icon size=""></size-icon>
  </template>
</test-fixture>

With a test suite as follows:

  suite('when no icon size or incorrect icon size is provided', function() {
    var el;
    setup(function() {
      el = fixture('hasWrongSize');
    });
    test('when the "size" property is incorrect the inner html equals "?"', function(done) {
      flush(function() {
        var domEl = Polymer.dom(el.root).querySelector('#icon:before');
        expect(domEl.innerHTML).to.equal('?');
        done();
      });
    });
  });

The Local DOM of the custom element is as follows:

<template>
  <template is="dom-if" if="{{ !sizeName }}">
    <span id="icon" class$="{{size}}"></span>
  </template>
  <span id="name">{{ sizeName }}</span>
</template>

I am trying to get the :before pseudo-element with the ID of #icon but WCT isn't able to find it and domEl in the test suite is equating to null.

What am I doing wrong?

tony19
  • 125,647
  • 18
  • 229
  • 307
AJStacy
  • 5,175
  • 4
  • 17
  • 31

2 Answers2

1

UPDATE The question was updated to include the complete code of the test-fixture, which revealed the root cause.

As you noted in your own answer:

  1. I have two template tags so it did not know which element to target.

That alone is the cause of your problem, and the pseudo-element actually had nothing to do with it. You'll find that if you remove one of the templates from the test-fixture, your original code would've worked (and would've been cleaner than what you have now IMO).

The test-fixture should only contain one template with a fixed state of your custom element as a baseline (the most common denominator) for your tests. In this case, that baseline is a single size-icon, but it seems you're adding a template for each possible test case, but that setup should really occur in the test script like this:

<test-fixture id="basic">
  <template>
    <size-icon></size-icon>
  </template>
</test-fixture>

<script>
  suite('size-icon', function() {
    var el;

    setup(function() {
      el = fixture('basic');
    });

    function expectIconEquals(done, contents) {
      flush(function() {
        var domEl = Polymer.dom(el.root).querySelector('#icon');
        expect(domEl.innerHTML).to.equal(contents);
        done();
      });
    }

    test('when the "size" property is empty, the inner html equals "?"', function(done) {
      el.size = "";
      expectIconEquals(done, '?');
    });

    test('when the "size" property is invalid, the inner html equals "?"', function(done) {
      el.size = "asdf";
      expectIconEquals(done, '?');
    });

    test('when the "size" property is valid, the inner html equals "<some valid content>"', function(done) {
      el.size = "12%";
      expectIconEquals(done, '<some valid content>');
    });
  });
</script>

Answer to original question:

In your code, the span#icon element is only stamped when sizeName is not undefined and is empty. The binding evaluation is similar to that of computed bindings (even though that's not explicitly stated for non-computed bindings):

The computing function is not called until all dependent properties are defined (!=undefined). So each dependent properties should have a default value defined in properties (or otherwise be initialized to a non-undefined value) to ensure the function value is computed.

To resolve the issue, set sizeName to the empty string in your test before flushing the template.

suite('when no icon size or incorrect icon size is provided', function() {
  var el;
  setup(function() {
    el = fixture('hasWrongSize');
  });
  test('when the "size" property is incorrect the inner html equals "?"', function(done) {

    // #icon exists only when sizeName is empty string
    el.sizeName = "";

    flush(function() {
      var domEl = Polymer.dom(el.root).querySelector('#icon');
      expect(domEl).to.not.be.null;
      expect(domEl.innerHTML).to.equal('?');
      done();
    });
  });
});

Alternatively, you could initialize sizeName inside your dom-module:

Polymer({
  is: 'size-icon',
  properties: {
    sizeName: {
      type: String,
      value: function() { return ""; }
    }
  }
});
tony19
  • 125,647
  • 18
  • 229
  • 307
  • I'm sorry, I wasn't specific enough in my code. I will update it with my property declarations. I was already setting a default value equal to an empty string. However, the error comes on the domEl variable. It is equating to Null. – AJStacy May 31 '16 at 14:02
  • I'm having trouble reproducing the problem. I've tried Chrome 53 & 51, Firefox 46.0.1, Safari 9.1 on OS X El Capitan. What's your test environment? And which browsers are failing? – tony19 May 31 '16 at 21:02
  • I discovered the solution to my problem. I did not provide you with a clear enough code sample. I made the edits and posted the solution I discovered. – AJStacy Jun 01 '16 at 13:34
1

I solved the problem. I was using a ':before' element to fill the content of the #icon span. When trying to select it I ran into two issues:

  1. I have two template tags so it did not know which element to target.
  2. I was incorrectly selecting a pseudo-element.

Here is the code I used to properly select and test the content attribute of the pseudo-element.

  var getBeforeText = function(el) {
    return window.getComputedStyle(
      getIcon(el), ':before'
    ).getPropertyValue('content').replace(/"/g, "").replace(/'/g, "");
  };

  var getIcon = function(el) {
    return Polymer.dom(el.root).querySelector('#icon');
  };

  suite('when no icon size or incorrect icon size is provided', function() {
    var el;
    setup(function() {
      el = fixture('hasWrongSize');
    });
    test('when the "size" property is incorrect the inner html equals "?"', function(done) {
      flush(function() {
        expect(getBeforeText(el)).to.equal('?');
        done();
      });
    });
  });

Pseudo-elements are not directly selectable with plain JS because they are not part of the DOM.

AJStacy
  • 5,175
  • 4
  • 17
  • 31
  • The root cause is that `test-fixture` does not handle multiple templates, and the pseudo-element actually has nothing to do with it. Your original code would've worked if you set up `test-fixture` with only one template. – tony19 Jun 01 '16 at 19:29
  • Once I had solved the test-fixture issue which was causing `el` to return null I still couldn't get the current text-content of the element. It was returning `''`. After poking around I discovered that the pseudo-element isn't targetable with vanilla JS as a node. – AJStacy Jun 01 '16 at 20:30