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