<!doctype html> <!– @license Copyright © 2015 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at polymer.github.io/LICENSE.txt The complete set of authors may be found at polymer.github.io/AUTHORS.txt The complete set of contributors may be found at 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 polymer.github.io/PATENTS.txt –> <html> <head>

<meta charset="UTF-8">
<title>paper-tooltip tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../web-component-tester/browser.js"></script>
<script src="../../test-fixture/test-fixture-mocha.js"></script>
<script src="../../iron-test-helpers/mock-interactions.js"></script>

<link rel="import" href="../../test-fixture/test-fixture.html">
<link rel="import" href="../paper-tooltip.html">
<link rel="import" href="test-button.html">

</head> <style>

body {
  margin: 0;
  padding: 0;
}
#target {
  width: 100px;
  height: 20px;
  background-color: red;
}
paper-tooltip {
  width: 70px;
  height: 30px;
}

.wide {
  width: 200px;
}

[hidden] {
  display: none;
}

</style>

<body>

<test-fixture id="basic">
  <template>
    <div>
      <div id="target"></div>
      <paper-tooltip for="target" animation-delay="0">Tooltip text</paper-tooltip>
    </div>
  </template>
</test-fixture>

<test-fixture id="fitted">
  <template>
    <div>
      <div id="target" style="position:absolute"></div>
      <paper-tooltip for="target" class="wide" fit-to-visible-bounds>Tooltip text</paper-tooltip>
    </div>
  </template>
</test-fixture>

<test-fixture id="no-text">
  <template>
    <div>
      <div id="target"></div>
      <paper-tooltip for="target"></paper-tooltip>
    </div>
  </template>
</test-fixture>

<test-fixture id="dynamic">
  <template>
    <div>
      <div id="target"></div>
      <paper-tooltip>Tooltip text</paper-tooltip>
    </div>
  </template>
</test-fixture>

<test-fixture id="custom">
  <template>
    <test-button></test-button>
  </template>
</test-fixture>

<test-fixture id="no-offset-parent">
  <template>
    <div>
      <div id="target"></div>
      <paper-tooltip for="target" animation-delay="0" hidden></paper-tooltip>
    </div>
  </template>
</test-fixture>

<test-fixture id="manual-mode">
  <template>
    <div>
      <div id="target"></div>
      <paper-tooltip for="target" manual-mode>Text</paper-tooltip>
    </div>
  </template>
</test-fixture>

<script>
  function isHidden(element) {
    var rect = element.getBoundingClientRect();
    return (rect.width == 0 && rect.height == 0);
  }

  suite('basic', function() {
    test('tooltip is shown when target is focused', function() {
      var f = fixture('no-text');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isTrue(isHidden(actualTooltip));
    });

    test('tooltip is not shown if empty', function() {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));
    });

    test('tooltip doesn\'t throw an exception if it has no offsetParent', function() {
      var f = fixture('no-offset-parent');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));
      tooltip.updatePosition();
      tooltip.show();

      // Doesn't get shown since there's no position computed.
      assert.isTrue(isHidden(actualTooltip));
    });

    test('tooltip is positioned correctly (bottom)', function() {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var divRect = target.getBoundingClientRect();
      expect(divRect.width).to.be.equal(100);
      expect(divRect.height).to.be.equal(20);

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.width).to.be.equal(70);
      expect(contentRect.height).to.be.equal(30);

      // The target div width is 100, and the tooltip width is 70, and
      // it's centered. The height of the target div is 20, and the
      // tooltip is 14px below.
      expect(contentRect.left).to.be.equal((100 - 70)/2);
      expect(contentRect.top).to.be.equal(20 + 14);

      // Also check the math, just in case.
      expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
      expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
    });

    test('tooltip is positioned correctly (top)', function() {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');
      tooltip.position = 'top';

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var divRect = target.getBoundingClientRect();
      expect(divRect.width).to.be.equal(100);
      expect(divRect.height).to.be.equal(20);

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.width).to.be.equal(70);
      expect(contentRect.height).to.be.equal(30);

      // The target div width is 100, and the tooltip width is 70, and
      // it's centered. The height of the tooltip is 30, and the
      // tooltip is 14px above the target.
      expect(contentRect.left).to.be.equal((100 - 70)/2);
      expect(contentRect.top).to.be.equal(0 - 30 - 14);

      // Also check the math, just in case.
      expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
      expect(contentRect.top).to.be.equal(0 - contentRect.height - tooltip.offset);
    });

    test('tooltip is positioned correctly (right)', function() {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');
      tooltip.position = 'right';

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var divRect = target.getBoundingClientRect();
      expect(divRect.width).to.be.equal(100);
      expect(divRect.height).to.be.equal(20);

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.width).to.be.equal(70);
      expect(contentRect.height).to.be.equal(30);

      // The target div width is 100, and the tooltip is 14px to the right.
      // The target div height is 20, the height of the tooltip is 20px, and
      // the tooltip is centered.
      expect(contentRect.left).to.be.equal(100 + 14);
      expect(contentRect.top).to.be.equal((20 - 30)/2);

      // Also check the math, just in case.
      expect(contentRect.left).to.be.equal(divRect.width + tooltip.offset);
      expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
    });

    test('tooltip is positioned correctly (left)', function() {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');
      tooltip.position = 'left';

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var divRect = target.getBoundingClientRect();
      expect(divRect.width).to.be.equal(100);
      expect(divRect.height).to.be.equal(20);

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.width).to.be.equal(70);
      expect(contentRect.height).to.be.equal(30);

      // The tooltip width is 70px, and the tooltip is 14px to the left of the target.
      // The target div height is 20, the height of the tooltip is 20px, and
      // the tooltip is centered.
      expect(contentRect.left).to.be.equal(0 - 70 - 14);
      expect(contentRect.top).to.be.equal((20 - 30)/2);

      // Also check the math, just in case.
      expect(contentRect.left).to.be.equal(0 - contentRect.width - tooltip.offset);
      expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
    });

    test('tooltip is fitted correctly if out of bounds', function() {
      var f = fixture('fitted');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');
      target.style.top = 0;
      target.style.left = 0;

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var contentRect = tooltip.getBoundingClientRect();
      var divRect = target.getBoundingClientRect();

      // Should be fitted on the left side.
      expect(contentRect.left).to.be.equal(0);
      expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
    });

    test('tooltip is positioned correctly after being dynamically set', function() {
      var f = fixture('dynamic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      // Skip animations in this test, which means we'll show and hide
      // the tooltip manually, instead of calling focus and blur.

      // The tooltip is shown because it's a sibling of the target,
      // but it's positioned incorrectly
      tooltip.toggleClass('hidden', false, actualTooltip);
      assert.isFalse(isHidden(actualTooltip));

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.left).to.not.be.equal((100 - 70)/2);

      tooltip.for = 'target';

      // The tooltip needs to hide before it gets repositioned.
      tooltip.toggleClass('hidden', true, actualTooltip);
      tooltip.updatePosition();
      tooltip.toggleClass('hidden', false, actualTooltip);
      assert.isFalse(isHidden(actualTooltip));

      // The target div width is 100, and the tooltip width is 70, and
      // it's centered. The height of the target div is 20, and the
      // tooltip is 14px below.
      contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.left).to.be.equal((100 - 70)/2);
      expect(contentRect.top).to.be.equal(20 + 14);
    });

    test('tooltip is hidden after target is blurred', function(done) {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));
      // Simulate but don't actually run the entry animation.
      tooltip.toggleClass('hidden', false, actualTooltip);
      tooltip._showing = true;
      assert.isFalse(isHidden(actualTooltip));

      tooltip.addEventListener('neon-animation-finish', function() {
        assert.isTrue(isHidden(actualTooltip));
        done();
      });
      MockInteractions.blur(target);
    });

    test('tooltip unlistens to target on detach', function(done) {
      var f = fixture('basic');
      var target = f.querySelector('#target');
      var tooltip = f.querySelector('paper-tooltip');

      sinon.spy(tooltip, 'show');

      MockInteractions.focus(target);
      expect(tooltip.show.callCount).to.be.equal(1);

      MockInteractions.focus(target);
      expect(tooltip.show.callCount).to.be.equal(2);

      f.removeChild(tooltip);

      setTimeout(function() {
        // No more listener means no more calling show.
        MockInteractions.focus(target);
        expect(tooltip.show.callCount).to.be.equal(2);
        done();
      }, 200);
    });

    test('tooltip ignores events in manual-mode', function() {
      var f = fixture('manual-mode');

      var tooltip = f.querySelector('paper-tooltip');
      assert.isTrue(tooltip.manualMode);

      tooltip.show();
      assert.isTrue(tooltip._showing);

      sinon.spy(tooltip, 'hide');

      tooltip.fire('mouseenter');

      var target = f.querySelector('#target');
      target.dispatchEvent(new CustomEvent('mouseenter'));
      target.dispatchEvent(new CustomEvent('focus'));
      target.dispatchEvent(new CustomEvent('mouseleave'));
      target.dispatchEvent(new CustomEvent('blur'));
      target.dispatchEvent(new CustomEvent('tap'));

      expect(tooltip.hide.callCount).to.be.equal(0);
    });

    test('changing manual-mode toggles event listeners', function() {
      var f = fixture('manual-mode');

      var tooltip = f.querySelector('paper-tooltip');
      assert.isTrue(tooltip.manualMode);

      sinon.spy(tooltip, '_addListeners');
      sinon.spy(tooltip, '_removeListeners');
      expect(tooltip._addListeners.callCount).to.be.equal(0);
      expect(tooltip._removeListeners.callCount).to.be.equal(0);

      tooltip.manualMode = false;
      expect(tooltip._addListeners.callCount).to.be.equal(1);
      expect(tooltip._removeListeners.callCount).to.be.equal(0);

      tooltip.manualMode = true;
      expect(tooltip._addListeners.callCount).to.be.equal(1);
      expect(tooltip._removeListeners.callCount).to.be.equal(1);
    });

    test('changing for= re-targets event listeners', function() {
      var f = fixture('dynamic');
      var tooltip = f.querySelector('paper-tooltip');

      sinon.spy(tooltip, '_addListeners');
      sinon.spy(tooltip, '_removeListeners');

      expect(tooltip._removeListeners.callCount).to.be.equal(0);
      expect(tooltip._addListeners.callCount).to.be.equal(0);

      tooltip.for = 'target';

      expect(tooltip._removeListeners.callCount).to.be.equal(1);
      expect(tooltip._addListeners.callCount).to.be.equal(1);
    });
  });

  suite('tooltip is inside a custom element', function() {
    var f, tooltip, target;

    setup(function() {
      f = fixture('custom');
      target = f.$.button;
      tooltip = f.$.buttonTooltip;
    });

    test('tooltip is shown when target is focused', function() {
      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));
    });

    test('tooltip is positioned correctly', function() {
      var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
      assert.isTrue(isHidden(actualTooltip));

      MockInteractions.focus(target);
      assert.isFalse(isHidden(actualTooltip));

      var divRect = target.getBoundingClientRect();
      expect(divRect.width).to.be.equal(100);
      expect(divRect.height).to.be.equal(20);

      var contentRect = tooltip.getBoundingClientRect();
      expect(contentRect.width).to.be.equal(70);
      expect(contentRect.height).to.be.equal(30);

      // The target div width is 100, and the tooltip width is 70, and
      // it's centered. The height of the target div is 20, and the
      // tooltip is 14px below.
      expect(contentRect.left).to.be.equal((100 - 70)/2);
      expect(contentRect.top).to.be.equal(20 + 14);

      // Also check the math, just in case.
      expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
      expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
    });
  });

  suite('a11y', function() {
    test('has aria role "tooltip"', function() {
      var f = fixture('basic');
      var tooltip = f.querySelector('paper-tooltip');

      assert.isTrue(tooltip.getAttribute('role') == 'tooltip');
    });

    var ignoredRules = ['roleTooltipRequiresDescribedby'];

    a11ySuite('basic', ignoredRules);
    a11ySuite('fitted', ignoredRules);
    a11ySuite('no-text', ignoredRules);
    a11ySuite('dynamic', ignoredRules);
    a11ySuite('custom', ignoredRules);
  });
</script>

</body> </html>