1<!doctype html>
2<!--
3@license
4Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8Code distributed by Google as part of the polymer project is also
9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10-->
11<html>
12<head>
13  <meta charset="UTF-8">
14  <title>paper-tooltip tests</title>
15  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
16
17  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
18  <script src="../../web-component-tester/browser.js"></script>
19  <script src="../../test-fixture/test-fixture-mocha.js"></script>
20  <script src="../../iron-test-helpers/mock-interactions.js"></script>
21
22  <link rel="import" href="../../test-fixture/test-fixture.html">
23  <link rel="import" href="../paper-tooltip.html">
24  <link rel="import" href="test-button.html">
25  <link rel="import" href="test-icon.html">
26
27</head>
28<style>
29  body {
30    margin: 0;
31    padding: 0;
32  }
33  #target {
34    width: 100px;
35    height: 20px;
36    background-color: red;
37  }
38  paper-tooltip {
39    width: 70px;
40    height: 30px;
41  }
42
43  .wide {
44    width: 200px;
45  }
46
47  [hidden] {
48    display: none;
49  }
50</style>
51
52<body>
53
54  <test-fixture id="basic">
55    <template>
56      <div>
57        <div id="target"></div>
58        <paper-tooltip for="target" animation-delay="0">Tooltip text</paper-tooltip>
59      </div>
60    </template>
61  </test-fixture>
62
63  <test-fixture id="fitted">
64    <template>
65      <div>
66        <div id="target" style="position:absolute"></div>
67        <paper-tooltip for="target" class="wide" fit-to-visible-bounds>Tooltip text</paper-tooltip>
68      </div>
69    </template>
70  </test-fixture>
71
72  <test-fixture id="no-text">
73    <template>
74      <div>
75        <div id="target"></div>
76        <paper-tooltip for="target"></paper-tooltip>
77      </div>
78    </template>
79  </test-fixture>
80
81  <test-fixture id="dynamic">
82    <template>
83      <div>
84        <div id="target"></div>
85        <paper-tooltip>Tooltip text</paper-tooltip>
86      </div>
87    </template>
88  </test-fixture>
89
90  <test-fixture id="custom">
91    <template>
92      <test-button></test-button>
93    </template>
94  </test-fixture>
95
96  <test-fixture id="custom-with-content">
97    <template>
98      <test-icon>Tooltip text</test-icon>
99    </template>
100  </test-fixture>
101
102  <test-fixture id="no-offset-parent">
103    <template>
104      <div>
105        <div id="target"></div>
106        <paper-tooltip for="target" animation-delay="0" hidden></paper-tooltip>
107      </div>
108    </template>
109  </test-fixture>
110
111  <test-fixture id="manual-mode">
112    <template>
113      <div>
114        <div id="target"></div>
115        <paper-tooltip for="target" manual-mode>Text</paper-tooltip>
116      </div>
117    </template>
118  </test-fixture>
119
120  <script>
121    function isHidden(element) {
122      var rect = element.getBoundingClientRect();
123      return (rect.width == 0 && rect.height == 0);
124    }
125
126    suite('basic', function() {
127      test('tooltip is shown when target is focused', function() {
128        var f = fixture('no-text');
129        var target = f.querySelector('#target');
130        var tooltip = f.querySelector('paper-tooltip');
131
132        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
133        assert.isTrue(isHidden(actualTooltip));
134
135        MockInteractions.focus(target);
136        assert.isTrue(isHidden(actualTooltip));
137      });
138
139      test('tooltip is not shown if empty', function() {
140        var f = fixture('basic');
141        var target = f.querySelector('#target');
142        var tooltip = f.querySelector('paper-tooltip');
143
144        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
145        assert.isTrue(isHidden(actualTooltip));
146
147        MockInteractions.focus(target);
148        assert.isFalse(isHidden(actualTooltip));
149      });
150
151      test('tooltip doesn\'t throw an exception if it has no offsetParent', function() {
152        var f = fixture('no-offset-parent');
153        var target = f.querySelector('#target');
154        var tooltip = f.querySelector('paper-tooltip');
155
156        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
157        assert.isTrue(isHidden(actualTooltip));
158        tooltip.updatePosition();
159        tooltip.show();
160
161        // Doesn't get shown since there's no position computed.
162        assert.isTrue(isHidden(actualTooltip));
163      });
164
165      test('tooltip is positioned correctly (bottom)', function() {
166        var f = fixture('basic');
167        var target = f.querySelector('#target');
168        var tooltip = f.querySelector('paper-tooltip');
169
170        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
171        assert.isTrue(isHidden(actualTooltip));
172
173        MockInteractions.focus(target);
174        assert.isFalse(isHidden(actualTooltip));
175
176        var divRect = target.getBoundingClientRect();
177        expect(divRect.width).to.be.equal(100);
178        expect(divRect.height).to.be.equal(20);
179
180        var contentRect = tooltip.getBoundingClientRect();
181        expect(contentRect.width).to.be.equal(70);
182        expect(contentRect.height).to.be.equal(30);
183
184        // The target div width is 100, and the tooltip width is 70, and
185        // it's centered. The height of the target div is 20, and the
186        // tooltip is 14px below.
187        expect(contentRect.left).to.be.equal((100 - 70)/2);
188        expect(contentRect.top).to.be.equal(20 + 14);
189
190        // Also check the math, just in case.
191        expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
192        expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
193      });
194
195      test('tooltip is positioned correctly (top)', function() {
196        var f = fixture('basic');
197        var target = f.querySelector('#target');
198        var tooltip = f.querySelector('paper-tooltip');
199        tooltip.position = 'top';
200
201        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
202        assert.isTrue(isHidden(actualTooltip));
203
204        MockInteractions.focus(target);
205        assert.isFalse(isHidden(actualTooltip));
206
207        var divRect = target.getBoundingClientRect();
208        expect(divRect.width).to.be.equal(100);
209        expect(divRect.height).to.be.equal(20);
210
211        var contentRect = tooltip.getBoundingClientRect();
212        expect(contentRect.width).to.be.equal(70);
213        expect(contentRect.height).to.be.equal(30);
214
215        // The target div width is 100, and the tooltip width is 70, and
216        // it's centered. The height of the tooltip is 30, and the
217        // tooltip is 14px above the target.
218        expect(contentRect.left).to.be.equal((100 - 70)/2);
219        expect(contentRect.top).to.be.equal(0 - 30 - 14);
220
221        // Also check the math, just in case.
222        expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
223        expect(contentRect.top).to.be.equal(0 - contentRect.height - tooltip.offset);
224      });
225
226      test('tooltip is positioned correctly (right)', function() {
227        var f = fixture('basic');
228        var target = f.querySelector('#target');
229        var tooltip = f.querySelector('paper-tooltip');
230        tooltip.position = 'right';
231
232        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
233        assert.isTrue(isHidden(actualTooltip));
234
235        MockInteractions.focus(target);
236        assert.isFalse(isHidden(actualTooltip));
237
238        var divRect = target.getBoundingClientRect();
239        expect(divRect.width).to.be.equal(100);
240        expect(divRect.height).to.be.equal(20);
241
242        var contentRect = tooltip.getBoundingClientRect();
243        expect(contentRect.width).to.be.equal(70);
244        expect(contentRect.height).to.be.equal(30);
245
246        // The target div width is 100, and the tooltip is 14px to the right.
247        // The target div height is 20, the height of the tooltip is 20px, and
248        // the tooltip is centered.
249        expect(contentRect.left).to.be.equal(100 + 14);
250        expect(contentRect.top).to.be.equal((20 - 30)/2);
251
252        // Also check the math, just in case.
253        expect(contentRect.left).to.be.equal(divRect.width + tooltip.offset);
254        expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
255      });
256
257      test('tooltip is positioned correctly (left)', function() {
258        var f = fixture('basic');
259        var target = f.querySelector('#target');
260        var tooltip = f.querySelector('paper-tooltip');
261        tooltip.position = 'left';
262
263        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
264        assert.isTrue(isHidden(actualTooltip));
265
266        MockInteractions.focus(target);
267        assert.isFalse(isHidden(actualTooltip));
268
269        var divRect = target.getBoundingClientRect();
270        expect(divRect.width).to.be.equal(100);
271        expect(divRect.height).to.be.equal(20);
272
273        var contentRect = tooltip.getBoundingClientRect();
274        expect(contentRect.width).to.be.equal(70);
275        expect(contentRect.height).to.be.equal(30);
276
277        // The tooltip width is 70px, and the tooltip is 14px to the left of the target.
278        // The target div height is 20, the height of the tooltip is 20px, and
279        // the tooltip is centered.
280        expect(contentRect.left).to.be.equal(0 - 70 - 14);
281        expect(contentRect.top).to.be.equal((20 - 30)/2);
282
283        // Also check the math, just in case.
284        expect(contentRect.left).to.be.equal(0 - contentRect.width - tooltip.offset);
285        expect(contentRect.top).to.be.equal((divRect.height - contentRect.height)/2);
286      });
287
288      test('tooltip is fitted correctly if out of bounds', function() {
289        var f = fixture('fitted');
290        var target = f.querySelector('#target');
291        var tooltip = f.querySelector('paper-tooltip');
292        target.style.top = 0;
293        target.style.left = 0;
294
295        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
296        assert.isTrue(isHidden(actualTooltip));
297
298        MockInteractions.focus(target);
299        assert.isFalse(isHidden(actualTooltip));
300
301        var contentRect = tooltip.getBoundingClientRect();
302        var divRect = target.getBoundingClientRect();
303
304        // Should be fitted on the left side.
305        expect(contentRect.left).to.be.equal(0);
306        expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
307      });
308
309      test('tooltip is positioned correctly after being dynamically set', function() {
310        var f = fixture('dynamic');
311        var target = f.querySelector('#target');
312        var tooltip = f.querySelector('paper-tooltip');
313
314        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
315        assert.isTrue(isHidden(actualTooltip));
316
317        // Skip animations in this test, which means we'll show and hide
318        // the tooltip manually, instead of calling focus and blur.
319
320        // The tooltip is shown because it's a sibling of the target,
321        // but it's positioned incorrectly
322        tooltip.toggleClass('hidden', false, actualTooltip);
323        assert.isFalse(isHidden(actualTooltip));
324
325        var contentRect = tooltip.getBoundingClientRect();
326        expect(contentRect.left).to.not.be.equal((100 - 70)/2);
327
328        tooltip.for = 'target';
329
330        // The tooltip needs to hide before it gets repositioned.
331        tooltip.toggleClass('hidden', true, actualTooltip);
332        tooltip.updatePosition();
333        tooltip.toggleClass('hidden', false, actualTooltip);
334        assert.isFalse(isHidden(actualTooltip));
335
336        // The target div width is 100, and the tooltip width is 70, and
337        // it's centered. The height of the target div is 20, and the
338        // tooltip is 14px below.
339        contentRect = tooltip.getBoundingClientRect();
340        expect(contentRect.left).to.be.equal((100 - 70)/2);
341        expect(contentRect.top).to.be.equal(20 + 14);
342      });
343
344      test('tooltip is hidden after target is blurred', function(done) {
345        var f = fixture('basic');
346        var target = f.querySelector('#target');
347        var tooltip = f.querySelector('paper-tooltip');
348
349        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
350        assert.isTrue(isHidden(actualTooltip));
351        // Simulate but don't actually run the entry animation.
352        tooltip.toggleClass('hidden', false, actualTooltip);
353        tooltip._showing = true;
354        assert.isFalse(isHidden(actualTooltip));
355
356        tooltip.addEventListener('neon-animation-finish', function() {
357          assert.isTrue(isHidden(actualTooltip));
358          done();
359        });
360        MockInteractions.blur(target);
361      });
362
363      test('tooltip unlistens to target on detach', function(done) {
364        var f = fixture('basic');
365        var target = f.querySelector('#target');
366        var tooltip = f.querySelector('paper-tooltip');
367
368        sinon.spy(tooltip, 'show');
369
370        MockInteractions.focus(target);
371        expect(tooltip.show.callCount).to.be.equal(1);
372
373        MockInteractions.focus(target);
374        expect(tooltip.show.callCount).to.be.equal(2);
375
376        f.removeChild(tooltip);
377
378        setTimeout(function() {
379          // No more listener means no more calling show.
380          MockInteractions.focus(target);
381          expect(tooltip.show.callCount).to.be.equal(2);
382          done();
383        }, 200);
384      });
385
386      test('tooltip ignores events in manual-mode', function() {
387        var f = fixture('manual-mode');
388
389        var tooltip = f.querySelector('paper-tooltip');
390        assert.isTrue(tooltip.manualMode);
391
392        tooltip.show();
393        assert.isTrue(tooltip._showing);
394
395        sinon.spy(tooltip, 'hide');
396
397        tooltip.fire('mouseenter');
398
399        var target = f.querySelector('#target');
400        target.dispatchEvent(new CustomEvent('mouseenter'));
401        target.dispatchEvent(new CustomEvent('focus'));
402        target.dispatchEvent(new CustomEvent('mouseleave'));
403        target.dispatchEvent(new CustomEvent('blur'));
404        target.dispatchEvent(new CustomEvent('tap'));
405
406        expect(tooltip.hide.callCount).to.be.equal(0);
407      });
408
409      test('changing manual-mode toggles event listeners', function() {
410        var f = fixture('manual-mode');
411
412        var tooltip = f.querySelector('paper-tooltip');
413        assert.isTrue(tooltip.manualMode);
414
415        sinon.spy(tooltip, '_addListeners');
416        sinon.spy(tooltip, '_removeListeners');
417        expect(tooltip._addListeners.callCount).to.be.equal(0);
418        expect(tooltip._removeListeners.callCount).to.be.equal(0);
419
420        tooltip.manualMode = false;
421        expect(tooltip._addListeners.callCount).to.be.equal(1);
422        expect(tooltip._removeListeners.callCount).to.be.equal(0);
423
424        tooltip.manualMode = true;
425        expect(tooltip._addListeners.callCount).to.be.equal(1);
426        expect(tooltip._removeListeners.callCount).to.be.equal(1);
427      });
428
429      test('changing for= re-targets event listeners', function() {
430        var f = fixture('dynamic');
431        var tooltip = f.querySelector('paper-tooltip');
432
433        sinon.spy(tooltip, '_addListeners');
434        sinon.spy(tooltip, '_removeListeners');
435
436        expect(tooltip._removeListeners.callCount).to.be.equal(0);
437        expect(tooltip._addListeners.callCount).to.be.equal(0);
438
439        tooltip.for = 'target';
440
441        expect(tooltip._removeListeners.callCount).to.be.equal(1);
442        expect(tooltip._addListeners.callCount).to.be.equal(1);
443      });
444    });
445
446    suite('tooltip is inside a custom element', function() {
447      var f, tooltip, target;
448
449      setup(function() {
450        f = fixture('custom');
451        target = f.$.button;
452        tooltip = f.$.buttonTooltip;
453      });
454
455      test('tooltip is shown when target is focused', function() {
456        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
457        assert.isTrue(isHidden(actualTooltip));
458
459        MockInteractions.focus(target);
460        assert.isFalse(isHidden(actualTooltip));
461      });
462
463      test('tooltip is positioned correctly', function() {
464        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
465        assert.isTrue(isHidden(actualTooltip));
466
467        MockInteractions.focus(target);
468        assert.isFalse(isHidden(actualTooltip));
469
470        var divRect = target.getBoundingClientRect();
471        expect(divRect.width).to.be.equal(100);
472        expect(divRect.height).to.be.equal(20);
473
474        var contentRect = tooltip.getBoundingClientRect();
475        expect(contentRect.width).to.be.equal(70);
476        expect(contentRect.height).to.be.equal(30);
477
478        // The target div width is 100, and the tooltip width is 70, and
479        // it's centered. The height of the target div is 20, and the
480        // tooltip is 14px below.
481        expect(contentRect.left).to.be.equal((100 - 70)/2);
482        expect(contentRect.top).to.be.equal(20 + 14);
483
484        // Also check the math, just in case.
485        expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
486        expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
487      });
488    });
489
490    suite('tooltip is inside a custom element with content', function() {
491      var f, tooltip, target;
492
493      setup(function() {
494        f = fixture('custom-with-content');
495        target = f.$.icon;
496        tooltip = f.$.iconTooltip;
497      });
498
499      test('tooltip is shown when target is focused', function() {
500        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
501        assert.isTrue(isHidden(actualTooltip));
502
503        MockInteractions.focus(target);
504        assert.isFalse(isHidden(actualTooltip));
505      });
506
507      test('tooltip is positioned correctly', function() {
508        var actualTooltip = Polymer.dom(tooltip.root).querySelector('#tooltip');
509        assert.isTrue(isHidden(actualTooltip));
510
511        MockInteractions.focus(target);
512        assert.isFalse(isHidden(actualTooltip));
513
514        var divRect = target.getBoundingClientRect();
515        expect(divRect.width).to.be.equal(100);
516        expect(divRect.height).to.be.equal(20);
517
518        var contentRect = tooltip.getBoundingClientRect();
519        expect(contentRect.width).to.be.equal(70);
520        expect(contentRect.height).to.be.equal(30);
521
522        // The target div width is 100, and the tooltip width is 70, and
523        // it's centered. The height of the target div is 20, and the
524        // tooltip is 14px below.
525        expect(contentRect.left).to.be.equal((100 - 70)/2);
526        expect(contentRect.top).to.be.equal(20 + 14);
527
528        // Also check the math, just in case.
529        expect(contentRect.left).to.be.equal((divRect.width - contentRect.width)/2);
530        expect(contentRect.top).to.be.equal(divRect.height + tooltip.offset);
531      });
532    });
533
534    suite('a11y', function() {
535      test('has aria role "tooltip"', function() {
536        var f = fixture('basic');
537        var tooltip = f.querySelector('paper-tooltip');
538
539        assert.isTrue(tooltip.getAttribute('role') == 'tooltip');
540      });
541
542      var ignoredRules = ['roleTooltipRequiresDescribedby'];
543
544      a11ySuite('basic', ignoredRules);
545      a11ySuite('fitted', ignoredRules);
546      a11ySuite('no-text', ignoredRules);
547      a11ySuite('dynamic', ignoredRules);
548      a11ySuite('custom', ignoredRules);
549    });
550  </script>
551</body>
552</html>
553