]> git.ipfire.org Git - thirdparty/bootstrap.git/blob - js/tests/unit/tooltip.spec.js
Update devDependencies (#35841)
[thirdparty/bootstrap.git] / js / tests / unit / tooltip.spec.js
1 import Tooltip from '../../src/tooltip'
2 import EventHandler from '../../src/dom/event-handler'
3 import { noop } from '../../src/util/index'
4 import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
5
6 describe('Tooltip', () => {
7 let fixtureEl
8
9 beforeAll(() => {
10 fixtureEl = getFixture()
11 })
12
13 afterEach(() => {
14 clearFixture()
15
16 for (const tooltipEl of document.querySelectorAll('.tooltip')) {
17 tooltipEl.remove()
18 }
19 })
20
21 describe('VERSION', () => {
22 it('should return plugin version', () => {
23 expect(Tooltip.VERSION).toEqual(jasmine.any(String))
24 })
25 })
26
27 describe('Default', () => {
28 it('should return plugin default config', () => {
29 expect(Tooltip.Default).toEqual(jasmine.any(Object))
30 })
31 })
32
33 describe('NAME', () => {
34 it('should return plugin name', () => {
35 expect(Tooltip.NAME).toEqual(jasmine.any(String))
36 })
37 })
38
39 describe('DATA_KEY', () => {
40 it('should return plugin data key', () => {
41 expect(Tooltip.DATA_KEY).toEqual('bs.tooltip')
42 })
43 })
44
45 describe('Event', () => {
46 it('should return plugin events', () => {
47 expect(Tooltip.Event).toEqual(jasmine.any(Object))
48 })
49 })
50
51 describe('EVENT_KEY', () => {
52 it('should return plugin event key', () => {
53 expect(Tooltip.EVENT_KEY).toEqual('.bs.tooltip')
54 })
55 })
56
57 describe('DefaultType', () => {
58 it('should return plugin default type', () => {
59 expect(Tooltip.DefaultType).toEqual(jasmine.any(Object))
60 })
61 })
62
63 describe('constructor', () => {
64 it('should take care of element either passed as a CSS selector or DOM element', () => {
65 fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title">'
66
67 const tooltipEl = fixtureEl.querySelector('#tooltipEl')
68 const tooltipBySelector = new Tooltip('#tooltipEl')
69 const tooltipByElement = new Tooltip(tooltipEl)
70
71 expect(tooltipBySelector._element).toEqual(tooltipEl)
72 expect(tooltipByElement._element).toEqual(tooltipEl)
73 })
74
75 it('should not take care of disallowed data attributes', () => {
76 fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip">'
77
78 const tooltipEl = fixtureEl.querySelector('a')
79 const tooltip = new Tooltip(tooltipEl)
80
81 expect(tooltip._config.sanitize).toBeTrue()
82 })
83
84 it('should convert title and content to string if numbers', () => {
85 fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
86
87 const tooltipEl = fixtureEl.querySelector('a')
88 const tooltip = new Tooltip(tooltipEl, {
89 title: 1,
90 content: 7
91 })
92
93 expect(tooltip._config.title).toEqual('1')
94 expect(tooltip._config.content).toEqual('7')
95 })
96
97 it('should enable selector delegation', () => {
98 return new Promise(resolve => {
99 fixtureEl.innerHTML = '<div></div>'
100
101 const containerEl = fixtureEl.querySelector('div')
102 const tooltipContainer = new Tooltip(containerEl, {
103 selector: 'a[rel="tooltip"]',
104 trigger: 'click'
105 })
106
107 containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
108
109 const tooltipInContainerEl = containerEl.querySelector('a')
110
111 tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
112 expect(document.querySelector('.tooltip')).not.toBeNull()
113 tooltipContainer.dispose()
114 resolve()
115 })
116
117 tooltipInContainerEl.click()
118 })
119 })
120
121 it('should create offset modifier when offset is passed as a function', () => {
122 return new Promise(resolve => {
123 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
124
125 const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
126 const tooltipEl = fixtureEl.querySelector('a')
127 const tooltip = new Tooltip(tooltipEl, {
128 offset: getOffset,
129 popperConfig: {
130 onFirstUpdate(state) {
131 expect(getOffset).toHaveBeenCalledWith({
132 popper: state.rects.popper,
133 reference: state.rects.reference,
134 placement: state.placement
135 }, tooltipEl)
136 resolve()
137 }
138 }
139 })
140
141 const offset = tooltip._getOffset()
142
143 expect(offset).toEqual(jasmine.any(Function))
144
145 tooltip.show()
146 })
147 })
148
149 it('should create offset modifier when offset option is passed in data attribute', () => {
150 fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip">'
151
152 const tooltipEl = fixtureEl.querySelector('a')
153 const tooltip = new Tooltip(tooltipEl)
154
155 expect(tooltip._getOffset()).toEqual([10, 20])
156 })
157
158 it('should allow to pass config to Popper with `popperConfig`', () => {
159 fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
160
161 const tooltipEl = fixtureEl.querySelector('a')
162 const tooltip = new Tooltip(tooltipEl, {
163 popperConfig: {
164 placement: 'left'
165 }
166 })
167
168 const popperConfig = tooltip._getPopperConfig('top')
169
170 expect(popperConfig.placement).toEqual('left')
171 })
172
173 it('should allow to pass config to Popper with `popperConfig` as a function', () => {
174 fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
175
176 const tooltipEl = fixtureEl.querySelector('a')
177 const getPopperConfig = jasmine.createSpy('getPopperConfig').and.returnValue({ placement: 'left' })
178 const tooltip = new Tooltip(tooltipEl, {
179 popperConfig: getPopperConfig
180 })
181
182 const popperConfig = tooltip._getPopperConfig('top')
183
184 expect(getPopperConfig).toHaveBeenCalled()
185 expect(popperConfig.placement).toEqual('left')
186 })
187
188 it('should use original title, if not "data-bs-title" is given', () => {
189 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
190
191 const tooltipEl = fixtureEl.querySelector('a')
192 const tooltip = new Tooltip(tooltipEl)
193
194 expect(tooltip._config.title).toEqual('Another tooltip')
195 })
196 })
197
198 describe('enable', () => {
199 it('should enable a tooltip', () => {
200 return new Promise(resolve => {
201 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
202
203 const tooltipEl = fixtureEl.querySelector('a')
204 const tooltip = new Tooltip(tooltipEl)
205
206 tooltip.enable()
207
208 tooltipEl.addEventListener('shown.bs.tooltip', () => {
209 expect(document.querySelector('.tooltip')).not.toBeNull()
210 resolve()
211 })
212
213 tooltip.show()
214 })
215 })
216 })
217
218 describe('disable', () => {
219 it('should disable tooltip', () => {
220 return new Promise(resolve => {
221 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
222
223 const tooltipEl = fixtureEl.querySelector('a')
224 const tooltip = new Tooltip(tooltipEl)
225
226 tooltip.disable()
227
228 tooltipEl.addEventListener('show.bs.tooltip', () => {
229 throw new Error('should not show a disabled tooltip')
230 })
231
232 tooltip.show()
233
234 setTimeout(() => {
235 expect().nothing()
236 resolve()
237 }, 10)
238 })
239 })
240 })
241
242 describe('toggleEnabled', () => {
243 it('should toggle enabled', () => {
244 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
245
246 const tooltipEl = fixtureEl.querySelector('a')
247 const tooltip = new Tooltip(tooltipEl)
248
249 expect(tooltip._isEnabled).toBeTrue()
250
251 tooltip.toggleEnabled()
252
253 expect(tooltip._isEnabled).toBeFalse()
254 })
255 })
256
257 describe('toggle', () => {
258 it('should do nothing if disabled', () => {
259 return new Promise(resolve => {
260 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
261
262 const tooltipEl = fixtureEl.querySelector('a')
263 const tooltip = new Tooltip(tooltipEl)
264
265 tooltip.disable()
266
267 tooltipEl.addEventListener('show.bs.tooltip', () => {
268 throw new Error('should not show a disabled tooltip')
269 })
270
271 tooltip.toggle()
272
273 setTimeout(() => {
274 expect().nothing()
275 resolve()
276 }, 10)
277 })
278 })
279
280 it('should show a tooltip', () => {
281 return new Promise(resolve => {
282 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
283
284 const tooltipEl = fixtureEl.querySelector('a')
285 const tooltip = new Tooltip(tooltipEl)
286
287 tooltipEl.addEventListener('shown.bs.tooltip', () => {
288 expect(document.querySelector('.tooltip')).not.toBeNull()
289 resolve()
290 })
291
292 tooltip.toggle()
293 })
294 })
295
296 it('should call toggle and show the tooltip when trigger is "click"', () => {
297 return new Promise(resolve => {
298 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
299
300 const tooltipEl = fixtureEl.querySelector('a')
301 const tooltip = new Tooltip(tooltipEl, {
302 trigger: 'click'
303 })
304
305 spyOn(tooltip, 'toggle').and.callThrough()
306
307 tooltipEl.addEventListener('shown.bs.tooltip', () => {
308 expect(tooltip.toggle).toHaveBeenCalled()
309 resolve()
310 })
311
312 tooltipEl.click()
313 })
314 })
315
316 it('should hide a tooltip', () => {
317 return new Promise(resolve => {
318 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
319
320 const tooltipEl = fixtureEl.querySelector('a')
321 const tooltip = new Tooltip(tooltipEl)
322
323 tooltipEl.addEventListener('shown.bs.tooltip', () => {
324 tooltip.toggle()
325 })
326
327 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
328 expect(document.querySelector('.tooltip')).toBeNull()
329 resolve()
330 })
331
332 tooltip.toggle()
333 })
334 })
335
336 it('should call toggle and hide the tooltip when trigger is "click"', () => {
337 return new Promise(resolve => {
338 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
339
340 const tooltipEl = fixtureEl.querySelector('a')
341 const tooltip = new Tooltip(tooltipEl, {
342 trigger: 'click'
343 })
344
345 spyOn(tooltip, 'toggle').and.callThrough()
346
347 tooltipEl.addEventListener('shown.bs.tooltip', () => {
348 tooltipEl.click()
349 })
350
351 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
352 expect(tooltip.toggle).toHaveBeenCalled()
353 resolve()
354 })
355
356 tooltipEl.click()
357 })
358 })
359 })
360
361 describe('dispose', () => {
362 it('should destroy a tooltip', () => {
363 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
364
365 const tooltipEl = fixtureEl.querySelector('a')
366 const addEventSpy = spyOn(tooltipEl, 'addEventListener').and.callThrough()
367 const removeEventSpy = spyOn(tooltipEl, 'removeEventListener').and.callThrough()
368
369 const tooltip = new Tooltip(tooltipEl)
370
371 expect(Tooltip.getInstance(tooltipEl)).toEqual(tooltip)
372
373 const expectedArgs = [
374 ['mouseover', jasmine.any(Function), jasmine.any(Boolean)],
375 ['mouseout', jasmine.any(Function), jasmine.any(Boolean)],
376 ['focusin', jasmine.any(Function), jasmine.any(Boolean)],
377 ['focusout', jasmine.any(Function), jasmine.any(Boolean)]
378 ]
379
380 expect(addEventSpy.calls.allArgs()).toEqual(expectedArgs)
381
382 tooltip.dispose()
383
384 expect(Tooltip.getInstance(tooltipEl)).toBeNull()
385 expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
386 })
387
388 it('should destroy a tooltip after it is shown and hidden', () => {
389 return new Promise(resolve => {
390 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
391
392 const tooltipEl = fixtureEl.querySelector('a')
393 const tooltip = new Tooltip(tooltipEl)
394
395 tooltipEl.addEventListener('shown.bs.tooltip', () => {
396 tooltip.hide()
397 })
398 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
399 tooltip.dispose()
400 expect(tooltip.tip).toBeNull()
401 expect(Tooltip.getInstance(tooltipEl)).toBeNull()
402 resolve()
403 })
404
405 tooltip.show()
406 })
407 })
408
409 it('should destroy a tooltip and remove it from the dom', () => {
410 return new Promise(resolve => {
411 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
412
413 const tooltipEl = fixtureEl.querySelector('a')
414 const tooltip = new Tooltip(tooltipEl)
415
416 tooltipEl.addEventListener('shown.bs.tooltip', () => {
417 expect(document.querySelector('.tooltip')).not.toBeNull()
418
419 tooltip.dispose()
420
421 expect(document.querySelector('.tooltip')).toBeNull()
422 resolve()
423 })
424
425 tooltip.show()
426 })
427 })
428 })
429
430 describe('show', () => {
431 it('should show a tooltip', () => {
432 return new Promise(resolve => {
433 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
434
435 const tooltipEl = fixtureEl.querySelector('a')
436 const tooltip = new Tooltip(tooltipEl)
437
438 tooltipEl.addEventListener('shown.bs.tooltip', () => {
439 const tooltipShown = document.querySelector('.tooltip')
440
441 expect(tooltipShown).not.toBeNull()
442 expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
443 expect(tooltipShown.getAttribute('id')).toContain('tooltip')
444 resolve()
445 })
446
447 tooltip.show()
448 })
449 })
450
451 it('should show a tooltip when hovering a child element', () => {
452 return new Promise(resolve => {
453 fixtureEl.innerHTML = [
454 '<a href="#" rel="tooltip" title="Another tooltip">',
455 ' <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">',
456 ' <rect width="100%" fill="#563d7c"/>',
457 ' <circle cx="50" cy="50" r="30" fill="#fff"/>',
458 ' </svg>',
459 '</a>'
460 ].join('')
461
462 const tooltipEl = fixtureEl.querySelector('a')
463 const tooltip = new Tooltip(tooltipEl)
464
465 spyOn(tooltip, 'show')
466
467 tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
468
469 setTimeout(() => {
470 expect(tooltip.show).toHaveBeenCalled()
471 resolve()
472 }, 0)
473 })
474 })
475
476 it('should show a tooltip on mobile', () => {
477 return new Promise(resolve => {
478 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
479
480 const tooltipEl = fixtureEl.querySelector('a')
481 const tooltip = new Tooltip(tooltipEl)
482 document.documentElement.ontouchstart = noop
483
484 spyOn(EventHandler, 'on').and.callThrough()
485
486 tooltipEl.addEventListener('shown.bs.tooltip', () => {
487 expect(document.querySelector('.tooltip')).not.toBeNull()
488 expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
489 document.documentElement.ontouchstart = undefined
490 resolve()
491 })
492
493 tooltip.show()
494 })
495 })
496
497 it('should show a tooltip relative to placement option', () => {
498 return new Promise(resolve => {
499 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
500
501 const tooltipEl = fixtureEl.querySelector('a')
502 const tooltip = new Tooltip(tooltipEl, {
503 placement: 'bottom'
504 })
505
506 tooltipEl.addEventListener('inserted.bs.tooltip', () => {
507 expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
508 })
509
510 tooltipEl.addEventListener('shown.bs.tooltip', () => {
511 expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
512 expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
513 resolve()
514 })
515
516 tooltip.show()
517 })
518 })
519
520 it('should not error when trying to show a tooltip that has been removed from the dom', () => {
521 return new Promise(resolve => {
522 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
523
524 const tooltipEl = fixtureEl.querySelector('a')
525 const tooltip = new Tooltip(tooltipEl)
526
527 const firstCallback = () => {
528 tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
529 let tooltipShown = document.querySelector('.tooltip')
530
531 tooltipShown.remove()
532
533 tooltipEl.addEventListener('shown.bs.tooltip', () => {
534 tooltipShown = document.querySelector('.tooltip')
535
536 expect(tooltipShown).not.toBeNull()
537 resolve()
538 })
539
540 tooltip.show()
541 }
542
543 tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
544
545 tooltip.show()
546 })
547 })
548
549 it('should show a tooltip with a dom element container', () => {
550 return new Promise(resolve => {
551 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
552
553 const tooltipEl = fixtureEl.querySelector('a')
554 const tooltip = new Tooltip(tooltipEl, {
555 container: fixtureEl
556 })
557
558 tooltipEl.addEventListener('shown.bs.tooltip', () => {
559 expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
560 resolve()
561 })
562
563 tooltip.show()
564 })
565 })
566
567 it('should show a tooltip with a jquery element container', () => {
568 return new Promise(resolve => {
569 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
570
571 const tooltipEl = fixtureEl.querySelector('a')
572 const tooltip = new Tooltip(tooltipEl, {
573 container: {
574 0: fixtureEl,
575 jquery: 'jQuery'
576 }
577 })
578
579 tooltipEl.addEventListener('shown.bs.tooltip', () => {
580 expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
581 resolve()
582 })
583
584 tooltip.show()
585 })
586 })
587
588 it('should show a tooltip with a selector in container', () => {
589 return new Promise(resolve => {
590 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
591
592 const tooltipEl = fixtureEl.querySelector('a')
593 const tooltip = new Tooltip(tooltipEl, {
594 container: '#fixture'
595 })
596
597 tooltipEl.addEventListener('shown.bs.tooltip', () => {
598 expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
599 resolve()
600 })
601
602 tooltip.show()
603 })
604 })
605
606 it('should show a tooltip with placement as a function', () => {
607 return new Promise(resolve => {
608 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
609
610 const spy = jasmine.createSpy('placement').and.returnValue('top')
611 const tooltipEl = fixtureEl.querySelector('a')
612 const tooltip = new Tooltip(tooltipEl, {
613 placement: spy
614 })
615
616 tooltipEl.addEventListener('shown.bs.tooltip', () => {
617 expect(document.querySelector('.tooltip')).not.toBeNull()
618 expect(spy).toHaveBeenCalled()
619 resolve()
620 })
621
622 tooltip.show()
623 })
624 })
625
626 it('should show a tooltip without the animation', () => {
627 return new Promise(resolve => {
628 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
629
630 const tooltipEl = fixtureEl.querySelector('a')
631 const tooltip = new Tooltip(tooltipEl, {
632 animation: false
633 })
634
635 tooltipEl.addEventListener('shown.bs.tooltip', () => {
636 const tip = document.querySelector('.tooltip')
637
638 expect(tip).not.toBeNull()
639 expect(tip).not.toHaveClass('fade')
640 resolve()
641 })
642
643 tooltip.show()
644 })
645 })
646
647 it('should throw an error the element is not visible', () => {
648 fixtureEl.innerHTML = '<a href="#" style="display: none" rel="tooltip" title="Another tooltip">'
649
650 const tooltipEl = fixtureEl.querySelector('a')
651 const tooltip = new Tooltip(tooltipEl)
652
653 try {
654 tooltip.show()
655 } catch (error) {
656 expect(error.message).toEqual('Please use show on visible elements')
657 }
658 })
659
660 it('should not show a tooltip if show.bs.tooltip is prevented', () => {
661 return new Promise(resolve => {
662 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
663
664 const tooltipEl = fixtureEl.querySelector('a')
665 const tooltip = new Tooltip(tooltipEl)
666
667 const expectedDone = () => {
668 setTimeout(() => {
669 expect(document.querySelector('.tooltip')).toBeNull()
670 resolve()
671 }, 10)
672 }
673
674 tooltipEl.addEventListener('show.bs.tooltip', ev => {
675 ev.preventDefault()
676 expectedDone()
677 })
678
679 tooltipEl.addEventListener('shown.bs.tooltip', () => {
680 throw new Error('Tooltip should not be shown')
681 })
682
683 tooltip.show()
684 })
685 })
686
687 it('should show tooltip if leave event hasn\'t occurred before delay expires', () => {
688 return new Promise(resolve => {
689 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
690
691 const tooltipEl = fixtureEl.querySelector('a')
692 const tooltip = new Tooltip(tooltipEl, {
693 delay: 150
694 })
695
696 spyOn(tooltip, 'show')
697
698 setTimeout(() => {
699 expect(tooltip.show).not.toHaveBeenCalled()
700 }, 100)
701
702 setTimeout(() => {
703 expect(tooltip.show).toHaveBeenCalled()
704 resolve()
705 }, 200)
706
707 tooltipEl.dispatchEvent(createEvent('mouseover'))
708 })
709 })
710
711 it('should not show tooltip if leave event occurs before delay expires', () => {
712 return new Promise(resolve => {
713 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
714
715 const tooltipEl = fixtureEl.querySelector('a')
716 const tooltip = new Tooltip(tooltipEl, {
717 delay: 150
718 })
719
720 spyOn(tooltip, 'show')
721
722 setTimeout(() => {
723 expect(tooltip.show).not.toHaveBeenCalled()
724 tooltipEl.dispatchEvent(createEvent('mouseover'))
725 }, 100)
726
727 setTimeout(() => {
728 expect(tooltip.show).toHaveBeenCalled()
729 expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
730 resolve()
731 }, 200)
732
733 tooltipEl.dispatchEvent(createEvent('mouseover'))
734 })
735 })
736
737 it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', () => {
738 return new Promise(resolve => {
739 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
740
741 const tooltipEl = fixtureEl.querySelector('a')
742 const tooltip = new Tooltip(tooltipEl, {
743 delay: {
744 show: 0,
745 hide: 150
746 }
747 })
748
749 setTimeout(() => {
750 expect(tooltip._getTipElement()).toHaveClass('show')
751 tooltipEl.dispatchEvent(createEvent('mouseout'))
752
753 setTimeout(() => {
754 expect(tooltip._getTipElement()).toHaveClass('show')
755 tooltipEl.dispatchEvent(createEvent('mouseover'))
756 }, 100)
757
758 setTimeout(() => {
759 expect(tooltip._getTipElement()).toHaveClass('show')
760 expect(document.querySelectorAll('.tooltip')).toHaveSize(1)
761 resolve()
762 }, 200)
763 }, 10)
764
765 tooltipEl.dispatchEvent(createEvent('mouseover'))
766 })
767 })
768
769 it('should not hide tooltip if leave event occurs and interaction remains inside trigger', () => {
770 return new Promise(resolve => {
771 fixtureEl.innerHTML = [
772 '<a href="#" rel="tooltip" title="Another tooltip">',
773 '<b>Trigger</b>',
774 'the tooltip',
775 '</a>'
776 ].join('')
777
778 const tooltipEl = fixtureEl.querySelector('a')
779 const tooltip = new Tooltip(tooltipEl)
780 const triggerChild = tooltipEl.querySelector('b')
781
782 spyOn(tooltip, 'hide').and.callThrough()
783
784 tooltipEl.addEventListener('mouseover', () => {
785 const moveMouseToChildEvent = createEvent('mouseout')
786 Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
787 value: triggerChild
788 })
789
790 tooltipEl.dispatchEvent(moveMouseToChildEvent)
791 })
792
793 tooltipEl.addEventListener('mouseout', () => {
794 expect(tooltip.hide).not.toHaveBeenCalled()
795 resolve()
796 })
797
798 tooltipEl.dispatchEvent(createEvent('mouseover'))
799 })
800 })
801
802 it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', () => {
803 return new Promise(resolve => {
804 // Style this tooltip to give it plenty of room for popper to do what it wants
805 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
806
807 const tooltipEl = fixtureEl.querySelector('a')
808 const tooltip = new Tooltip(tooltipEl)
809
810 spyOn(window, 'getComputedStyle').and.returnValue({
811 transitionDuration: '0.15s',
812 transitionDelay: '0s'
813 })
814
815 setTimeout(() => {
816 expect(tooltip._popper).not.toBeNull()
817 expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
818 tooltipEl.dispatchEvent(createEvent('mouseout'))
819
820 setTimeout(() => {
821 expect(tooltip._getTipElement()).not.toHaveClass('show')
822 tooltipEl.dispatchEvent(createEvent('mouseover'))
823 }, 100)
824
825 setTimeout(() => {
826 expect(tooltip._popper).not.toBeNull()
827 expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
828 resolve()
829 }, 200)
830 }, 10)
831
832 tooltipEl.dispatchEvent(createEvent('mouseover'))
833 })
834 })
835
836 it('should only trigger inserted event if a new tooltip element was created', () => {
837 return new Promise(resolve => {
838 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
839
840 const tooltipEl = fixtureEl.querySelector('a')
841 const tooltip = new Tooltip(tooltipEl)
842
843 spyOn(window, 'getComputedStyle').and.returnValue({
844 transitionDuration: '0.15s',
845 transitionDelay: '0s'
846 })
847
848 const insertedFunc = jasmine.createSpy()
849 tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
850
851 setTimeout(() => {
852 expect(insertedFunc).toHaveBeenCalledTimes(1)
853 tooltip.hide()
854
855 setTimeout(() => {
856 tooltip.show()
857 }, 100)
858
859 setTimeout(() => {
860 expect(insertedFunc).toHaveBeenCalledTimes(1)
861 resolve()
862 }, 200)
863 }, 0)
864
865 tooltip.show()
866 })
867 })
868
869 it('should show a tooltip with custom class provided in data attributes', () => {
870 return new Promise(resolve => {
871 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
872
873 const tooltipEl = fixtureEl.querySelector('a')
874 const tooltip = new Tooltip(tooltipEl)
875
876 tooltipEl.addEventListener('shown.bs.tooltip', () => {
877 const tip = document.querySelector('.tooltip')
878 expect(tip).not.toBeNull()
879 expect(tip).toHaveClass('custom-class')
880 resolve()
881 })
882
883 tooltip.show()
884 })
885 })
886
887 it('should show a tooltip with custom class provided as a string in config', () => {
888 return new Promise(resolve => {
889 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
890
891 const tooltipEl = fixtureEl.querySelector('a')
892 const tooltip = new Tooltip(tooltipEl, {
893 customClass: 'custom-class custom-class-2'
894 })
895
896 tooltipEl.addEventListener('shown.bs.tooltip', () => {
897 const tip = document.querySelector('.tooltip')
898 expect(tip).not.toBeNull()
899 expect(tip).toHaveClass('custom-class')
900 expect(tip).toHaveClass('custom-class-2')
901 resolve()
902 })
903
904 tooltip.show()
905 })
906 })
907
908 it('should show a tooltip with custom class provided as a function in config', () => {
909 return new Promise(resolve => {
910 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
911
912 const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
913 const tooltipEl = fixtureEl.querySelector('a')
914 const tooltip = new Tooltip(tooltipEl, {
915 customClass: spy
916 })
917
918 tooltipEl.addEventListener('shown.bs.tooltip', () => {
919 const tip = document.querySelector('.tooltip')
920 expect(tip).not.toBeNull()
921 expect(spy).toHaveBeenCalled()
922 expect(tip).toHaveClass('custom-class')
923 resolve()
924 })
925
926 tooltip.show()
927 })
928 })
929
930 it('should remove `title` attribute if exists', () => {
931 return new Promise(resolve => {
932 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
933
934 const tooltipEl = fixtureEl.querySelector('a')
935 const tooltip = new Tooltip(tooltipEl)
936
937 tooltipEl.addEventListener('shown.bs.tooltip', () => {
938 expect(tooltipEl.getAttribute('title')).toBeNull()
939 resolve()
940 })
941 tooltip.show()
942 })
943 })
944 })
945
946 describe('hide', () => {
947 it('should hide a tooltip', () => {
948 return new Promise(resolve => {
949 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
950
951 const tooltipEl = fixtureEl.querySelector('a')
952 const tooltip = new Tooltip(tooltipEl)
953
954 tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
955 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
956 expect(document.querySelector('.tooltip')).toBeNull()
957 expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
958 resolve()
959 })
960
961 tooltip.show()
962 })
963 })
964
965 it('should hide a tooltip on mobile', () => {
966 return new Promise(resolve => {
967 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
968
969 const tooltipEl = fixtureEl.querySelector('a')
970 const tooltip = new Tooltip(tooltipEl)
971
972 tooltipEl.addEventListener('shown.bs.tooltip', () => {
973 document.documentElement.ontouchstart = noop
974 spyOn(EventHandler, 'off')
975 tooltip.hide()
976 })
977
978 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
979 expect(document.querySelector('.tooltip')).toBeNull()
980 expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
981 document.documentElement.ontouchstart = undefined
982 resolve()
983 })
984
985 tooltip.show()
986 })
987 })
988
989 it('should hide a tooltip without animation', () => {
990 return new Promise(resolve => {
991 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
992
993 const tooltipEl = fixtureEl.querySelector('a')
994 const tooltip = new Tooltip(tooltipEl, {
995 animation: false
996 })
997
998 tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
999 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
1000 expect(document.querySelector('.tooltip')).toBeNull()
1001 expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
1002 resolve()
1003 })
1004
1005 tooltip.show()
1006 })
1007 })
1008
1009 it('should not hide a tooltip if hide event is prevented', () => {
1010 return new Promise(resolve => {
1011 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1012
1013 const assertDone = () => {
1014 setTimeout(() => {
1015 expect(document.querySelector('.tooltip')).not.toBeNull()
1016 resolve()
1017 }, 20)
1018 }
1019
1020 const tooltipEl = fixtureEl.querySelector('a')
1021 const tooltip = new Tooltip(tooltipEl, {
1022 animation: false
1023 })
1024
1025 tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
1026 tooltipEl.addEventListener('hide.bs.tooltip', event => {
1027 event.preventDefault()
1028 assertDone()
1029 })
1030 tooltipEl.addEventListener('hidden.bs.tooltip', () => {
1031 throw new Error('should not trigger hidden event')
1032 })
1033
1034 tooltip.show()
1035 })
1036 })
1037
1038 it('should not throw error running hide if popper hasn\'t been shown', () => {
1039 fixtureEl.innerHTML = '<div></div>'
1040
1041 const div = fixtureEl.querySelector('div')
1042 const tooltip = new Tooltip(div)
1043
1044 try {
1045 tooltip.hide()
1046 expect().nothing()
1047 } catch {
1048 throw new Error('should not throw error')
1049 }
1050 })
1051 })
1052
1053 describe('update', () => {
1054 it('should call popper update', () => {
1055 return new Promise(resolve => {
1056 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1057
1058 const tooltipEl = fixtureEl.querySelector('a')
1059 const tooltip = new Tooltip(tooltipEl)
1060
1061 tooltipEl.addEventListener('shown.bs.tooltip', () => {
1062 spyOn(tooltip._popper, 'update')
1063
1064 tooltip.update()
1065
1066 expect(tooltip._popper.update).toHaveBeenCalled()
1067 resolve()
1068 })
1069
1070 tooltip.show()
1071 })
1072 })
1073
1074 it('should do nothing if the tooltip is not shown', () => {
1075 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1076
1077 const tooltipEl = fixtureEl.querySelector('a')
1078 const tooltip = new Tooltip(tooltipEl)
1079
1080 tooltip.update()
1081 expect().nothing()
1082 })
1083 })
1084
1085 describe('_isWithContent', () => {
1086 it('should return true if there is content', () => {
1087 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1088
1089 const tooltipEl = fixtureEl.querySelector('a')
1090 const tooltip = new Tooltip(tooltipEl)
1091
1092 expect(tooltip._isWithContent()).toBeTrue()
1093 })
1094
1095 it('should return false if there is no content', () => {
1096 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="">'
1097
1098 const tooltipEl = fixtureEl.querySelector('a')
1099 const tooltip = new Tooltip(tooltipEl)
1100
1101 expect(tooltip._isWithContent()).toBeFalse()
1102 })
1103 })
1104
1105 describe('_getTipElement', () => {
1106 it('should create the tip element and return it', () => {
1107 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1108
1109 const tooltipEl = fixtureEl.querySelector('a')
1110 const tooltip = new Tooltip(tooltipEl)
1111
1112 spyOn(document, 'createElement').and.callThrough()
1113
1114 expect(tooltip._getTipElement()).toBeDefined()
1115 expect(document.createElement).toHaveBeenCalled()
1116 })
1117
1118 it('should return the created tip element', () => {
1119 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1120
1121 const tooltipEl = fixtureEl.querySelector('a')
1122 const tooltip = new Tooltip(tooltipEl)
1123
1124 const spy = spyOn(document, 'createElement').and.callThrough()
1125
1126 expect(tooltip._getTipElement()).toBeDefined()
1127 expect(spy).toHaveBeenCalled()
1128
1129 spy.calls.reset()
1130
1131 expect(tooltip._getTipElement()).toBeDefined()
1132 expect(spy).not.toHaveBeenCalled()
1133 })
1134 })
1135
1136 describe('setContent', () => {
1137 it('should set tip content', () => {
1138 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1139
1140 const tooltipEl = fixtureEl.querySelector('a')
1141 const tooltip = new Tooltip(tooltipEl, { animation: false })
1142
1143 const tip = tooltip._getTipElement()
1144
1145 tooltip.setContent(tip)
1146
1147 expect(tip).not.toHaveClass('show')
1148 expect(tip).not.toHaveClass('fade')
1149 expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
1150 })
1151
1152 it('should re-show tip if it was already shown', () => {
1153 fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
1154
1155 const tooltipEl = fixtureEl.querySelector('a')
1156 const tooltip = new Tooltip(tooltipEl)
1157 tooltip.show()
1158 const tip = () => tooltip._getTipElement()
1159
1160 expect(tip()).toHaveClass('show')
1161 tooltip.setContent({ '.tooltip-inner': 'foo' })
1162
1163 expect(tip()).toHaveClass('show')
1164 expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
1165 })
1166
1167 it('should keep tip hidden, if it was already hidden before', () => {
1168 fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
1169
1170 const tooltipEl = fixtureEl.querySelector('a')
1171 const tooltip = new Tooltip(tooltipEl)
1172 const tip = () => tooltip._getTipElement()
1173
1174 expect(tip()).not.toHaveClass('show')
1175 tooltip.setContent({ '.tooltip-inner': 'foo' })
1176
1177 expect(tip()).not.toHaveClass('show')
1178 expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
1179 })
1180
1181 it('"setContent" should keep the initial template', () => {
1182 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1183
1184 const tooltipEl = fixtureEl.querySelector('a')
1185 const tooltip = new Tooltip(tooltipEl)
1186
1187 tooltip.setContent({ '.tooltip-inner': 'foo' })
1188 const tip = tooltip._getTipElement()
1189
1190 expect(tip).toHaveClass('tooltip')
1191 expect(tip).toHaveClass('bs-tooltip-auto')
1192 expect(tip.querySelector('.tooltip-arrow')).not.toBeNull()
1193 expect(tip.querySelector('.tooltip-inner')).not.toBeNull()
1194 })
1195 })
1196
1197 describe('setContent', () => {
1198 it('should do nothing if the element is null', () => {
1199 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
1200
1201 const tooltipEl = fixtureEl.querySelector('a')
1202 const tooltip = new Tooltip(tooltipEl)
1203
1204 tooltip.setContent({ '.tooltip': null })
1205 expect().nothing()
1206 })
1207
1208 it('should do nothing if the content is a child of the element', () => {
1209 fixtureEl.innerHTML = [
1210 '<a href="#" rel="tooltip" title="Another tooltip">',
1211 ' <div id="childContent"></div>',
1212 '</a>'
1213 ].join('')
1214
1215 const tooltipEl = fixtureEl.querySelector('a')
1216 const childContent = fixtureEl.querySelector('div')
1217 const tooltip = new Tooltip(tooltipEl, {
1218 html: true
1219 })
1220
1221 tooltip._getTipElement().append(childContent)
1222 tooltip.setContent({ '.tooltip': childContent })
1223
1224 expect().nothing()
1225 })
1226
1227 it('should add the content as a child of the element for jQuery elements', () => {
1228 fixtureEl.innerHTML = [
1229 '<a href="#" rel="tooltip" title="Another tooltip">',
1230 ' <div id="childContent"></div>',
1231 '</a>'
1232 ].join('')
1233
1234 const tooltipEl = fixtureEl.querySelector('a')
1235 const childContent = fixtureEl.querySelector('div')
1236 const tooltip = new Tooltip(tooltipEl, {
1237 html: true
1238 })
1239
1240 tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } })
1241
1242 expect(childContent.parentNode).toEqual(tooltip._getTipElement())
1243 })
1244
1245 it('should add the child text content in the element', () => {
1246 fixtureEl.innerHTML = [
1247 '<a href="#" rel="tooltip" title="Another tooltip">',
1248 ' <div id="childContent">Tooltip</div>',
1249 '</a>'
1250 ].join('')
1251
1252 const tooltipEl = fixtureEl.querySelector('a')
1253 const childContent = fixtureEl.querySelector('div')
1254 const tooltip = new Tooltip(tooltipEl)
1255
1256 tooltip.setContent({ '.tooltip': childContent })
1257
1258 expect(childContent.textContent).toEqual(tooltip._getTipElement().textContent)
1259 })
1260
1261 it('should add html without sanitize it', () => {
1262 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
1263
1264 const tooltipEl = fixtureEl.querySelector('a')
1265 const tooltip = new Tooltip(tooltipEl, {
1266 sanitize: false,
1267 html: true
1268 })
1269
1270 tooltip.setContent({ '.tooltip': '<div id="childContent">Tooltip</div>' })
1271
1272 expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
1273 })
1274
1275 it('should add html sanitized', () => {
1276 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
1277
1278 const tooltipEl = fixtureEl.querySelector('a')
1279 const tooltip = new Tooltip(tooltipEl, {
1280 html: true
1281 })
1282
1283 const content = [
1284 '<div id="childContent">',
1285 ' <button type="button">test btn</button>',
1286 '</div>'
1287 ].join('')
1288
1289 tooltip.setContent({ '.tooltip': content })
1290 expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
1291 expect(tooltip._getTipElement().querySelector('button')).toBeNull()
1292 })
1293
1294 it('should add text content', () => {
1295 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
1296
1297 const tooltipEl = fixtureEl.querySelector('a')
1298 const tooltip = new Tooltip(tooltipEl)
1299
1300 tooltip.setContent({ '.tooltip': 'test' })
1301
1302 expect(tooltip._getTipElement().textContent).toEqual('test')
1303 })
1304 })
1305
1306 describe('_getTitle', () => {
1307 it('should return the title', () => {
1308 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
1309
1310 const tooltipEl = fixtureEl.querySelector('a')
1311 const tooltip = new Tooltip(tooltipEl)
1312
1313 expect(tooltip._getTitle()).toEqual('Another tooltip')
1314 })
1315
1316 it('should call title function', () => {
1317 fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
1318
1319 const tooltipEl = fixtureEl.querySelector('a')
1320 const tooltip = new Tooltip(tooltipEl, {
1321 title: () => 'test'
1322 })
1323
1324 expect(tooltip._getTitle()).toEqual('test')
1325 })
1326 })
1327
1328 describe('getInstance', () => {
1329 it('should return tooltip instance', () => {
1330 fixtureEl.innerHTML = '<div></div>'
1331
1332 const div = fixtureEl.querySelector('div')
1333 const alert = new Tooltip(div)
1334
1335 expect(Tooltip.getInstance(div)).toEqual(alert)
1336 expect(Tooltip.getInstance(div)).toBeInstanceOf(Tooltip)
1337 })
1338
1339 it('should return null when there is no tooltip instance', () => {
1340 fixtureEl.innerHTML = '<div></div>'
1341
1342 const div = fixtureEl.querySelector('div')
1343
1344 expect(Tooltip.getInstance(div)).toBeNull()
1345 })
1346 })
1347
1348 describe('aria-label', () => {
1349 it('should add the aria-label attribute for referencing original title', () => {
1350 return new Promise(resolve => {
1351 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
1352
1353 const tooltipEl = fixtureEl.querySelector('a')
1354 const tooltip = new Tooltip(tooltipEl)
1355
1356 tooltipEl.addEventListener('shown.bs.tooltip', () => {
1357 const tooltipShown = document.querySelector('.tooltip')
1358
1359 expect(tooltipShown).not.toBeNull()
1360 expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
1361 resolve()
1362 })
1363
1364 tooltip.show()
1365 })
1366 })
1367
1368 it('should not add the aria-label attribute if the attribute already exists', () => {
1369 return new Promise(resolve => {
1370 fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
1371
1372 const tooltipEl = fixtureEl.querySelector('a')
1373 const tooltip = new Tooltip(tooltipEl)
1374
1375 tooltipEl.addEventListener('shown.bs.tooltip', () => {
1376 const tooltipShown = document.querySelector('.tooltip')
1377
1378 expect(tooltipShown).not.toBeNull()
1379 expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
1380 resolve()
1381 })
1382
1383 tooltip.show()
1384 })
1385 })
1386
1387 it('should not add the aria-label attribute if the element has text content', () => {
1388 return new Promise(resolve => {
1389 fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
1390
1391 const tooltipEl = fixtureEl.querySelector('a')
1392 const tooltip = new Tooltip(tooltipEl)
1393
1394 tooltipEl.addEventListener('shown.bs.tooltip', () => {
1395 const tooltipShown = document.querySelector('.tooltip')
1396
1397 expect(tooltipShown).not.toBeNull()
1398 expect(tooltipEl.getAttribute('aria-label')).toBeNull()
1399 resolve()
1400 })
1401
1402 tooltip.show()
1403 })
1404 })
1405 })
1406
1407 describe('getOrCreateInstance', () => {
1408 it('should return tooltip instance', () => {
1409 fixtureEl.innerHTML = '<div></div>'
1410
1411 const div = fixtureEl.querySelector('div')
1412 const tooltip = new Tooltip(div)
1413
1414 expect(Tooltip.getOrCreateInstance(div)).toEqual(tooltip)
1415 expect(Tooltip.getInstance(div)).toEqual(Tooltip.getOrCreateInstance(div, {}))
1416 expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
1417 })
1418
1419 it('should return new instance when there is no tooltip instance', () => {
1420 fixtureEl.innerHTML = '<div></div>'
1421
1422 const div = fixtureEl.querySelector('div')
1423
1424 expect(Tooltip.getInstance(div)).toBeNull()
1425 expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
1426 })
1427
1428 it('should return new instance when there is no tooltip instance with given configuration', () => {
1429 fixtureEl.innerHTML = '<div></div>'
1430
1431 const div = fixtureEl.querySelector('div')
1432
1433 expect(Tooltip.getInstance(div)).toBeNull()
1434 const tooltip = Tooltip.getOrCreateInstance(div, {
1435 title: () => 'test'
1436 })
1437 expect(tooltip).toBeInstanceOf(Tooltip)
1438
1439 expect(tooltip._getTitle()).toEqual('test')
1440 })
1441
1442 it('should return the instance when exists without given configuration', () => {
1443 fixtureEl.innerHTML = '<div></div>'
1444
1445 const div = fixtureEl.querySelector('div')
1446 const tooltip = new Tooltip(div, {
1447 title: () => 'nothing'
1448 })
1449 expect(Tooltip.getInstance(div)).toEqual(tooltip)
1450
1451 const tooltip2 = Tooltip.getOrCreateInstance(div, {
1452 title: () => 'test'
1453 })
1454 expect(tooltip).toBeInstanceOf(Tooltip)
1455 expect(tooltip2).toEqual(tooltip)
1456
1457 expect(tooltip2._getTitle()).toEqual('nothing')
1458 })
1459 })
1460
1461 describe('jQueryInterface', () => {
1462 it('should create a tooltip', () => {
1463 fixtureEl.innerHTML = '<div></div>'
1464
1465 const div = fixtureEl.querySelector('div')
1466
1467 jQueryMock.fn.tooltip = Tooltip.jQueryInterface
1468 jQueryMock.elements = [div]
1469
1470 jQueryMock.fn.tooltip.call(jQueryMock)
1471
1472 expect(Tooltip.getInstance(div)).not.toBeNull()
1473 })
1474
1475 it('should not re create a tooltip', () => {
1476 fixtureEl.innerHTML = '<div></div>'
1477
1478 const div = fixtureEl.querySelector('div')
1479 const tooltip = new Tooltip(div)
1480
1481 jQueryMock.fn.tooltip = Tooltip.jQueryInterface
1482 jQueryMock.elements = [div]
1483
1484 jQueryMock.fn.tooltip.call(jQueryMock)
1485
1486 expect(Tooltip.getInstance(div)).toEqual(tooltip)
1487 })
1488
1489 it('should call a tooltip method', () => {
1490 fixtureEl.innerHTML = '<div></div>'
1491
1492 const div = fixtureEl.querySelector('div')
1493 const tooltip = new Tooltip(div)
1494
1495 spyOn(tooltip, 'show')
1496
1497 jQueryMock.fn.tooltip = Tooltip.jQueryInterface
1498 jQueryMock.elements = [div]
1499
1500 jQueryMock.fn.tooltip.call(jQueryMock, 'show')
1501
1502 expect(Tooltip.getInstance(div)).toEqual(tooltip)
1503 expect(tooltip.show).toHaveBeenCalled()
1504 })
1505
1506 it('should throw error on undefined method', () => {
1507 fixtureEl.innerHTML = '<div></div>'
1508
1509 const div = fixtureEl.querySelector('div')
1510 const action = 'undefinedMethod'
1511
1512 jQueryMock.fn.tooltip = Tooltip.jQueryInterface
1513 jQueryMock.elements = [div]
1514
1515 expect(() => {
1516 jQueryMock.fn.tooltip.call(jQueryMock, action)
1517 }).toThrowError(TypeError, `No method named "${action}"`)
1518 })
1519 })
1520 })