Policy 1707 commit to LF
[policy/engine.git] / POLICY-SDK-APP / src / main / webapp / app / policyApp / CSS / bootstrap / js / tests / unit / tooltip.js
1 $(function () {
2   'use strict';
3
4   QUnit.module('tooltip plugin')
5
6   QUnit.test('should be defined on jquery object', function (assert) {
7     assert.expect(1)
8     assert.ok($(document.body).tooltip, 'tooltip method is defined')
9   })
10
11   QUnit.module('tooltip', {
12     beforeEach: function () {
13       // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
14       $.fn.bootstrapTooltip = $.fn.tooltip.noConflict()
15     },
16     afterEach: function () {
17       $.fn.tooltip = $.fn.bootstrapTooltip
18       delete $.fn.bootstrapTooltip
19     }
20   })
21
22   QUnit.test('should provide no conflict', function (assert) {
23     assert.expect(1)
24     assert.strictEqual($.fn.tooltip, undefined, 'tooltip was set back to undefined (org value)')
25   })
26
27   QUnit.test('should return jquery collection containing the element', function (assert) {
28     assert.expect(2)
29     var $el = $('<div/>')
30     var $tooltip = $el.bootstrapTooltip()
31     assert.ok($tooltip instanceof $, 'returns jquery collection')
32     assert.strictEqual($tooltip[0], $el[0], 'collection contains element')
33   })
34
35   QUnit.test('should expose default settings', function (assert) {
36     assert.expect(1)
37     assert.ok($.fn.bootstrapTooltip.Constructor.DEFAULTS, 'defaults is defined')
38   })
39
40   QUnit.test('should empty title attribute', function (assert) {
41     assert.expect(1)
42     var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
43     assert.strictEqual($trigger.attr('title'), '', 'title attribute was emptied')
44   })
45
46   QUnit.test('should add data attribute for referencing original title', function (assert) {
47     assert.expect(1)
48     var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>').bootstrapTooltip()
49     assert.strictEqual($trigger.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
50   })
51
52   QUnit.test('should add aria-describedby to the trigger on show', function (assert) {
53     assert.expect(3)
54     var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
55       .bootstrapTooltip()
56       .appendTo('#qunit-fixture')
57       .bootstrapTooltip('show')
58
59     var id = $('.tooltip').attr('id')
60
61     assert.strictEqual($('#' + id).length, 1, 'has a unique id')
62     assert.strictEqual($('.tooltip').attr('aria-describedby'), $trigger.attr('id'), 'tooltip id and aria-describedby on trigger match')
63     assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
64   })
65
66   QUnit.test('should remove aria-describedby from trigger on hide', function (assert) {
67     assert.expect(2)
68     var $trigger = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
69       .bootstrapTooltip()
70       .appendTo('#qunit-fixture')
71
72     $trigger.bootstrapTooltip('show')
73     assert.ok($trigger[0].hasAttribute('aria-describedby'), 'trigger has aria-describedby')
74
75     $trigger.bootstrapTooltip('hide')
76     assert.ok(!$trigger[0].hasAttribute('aria-describedby'), 'trigger does not have aria-describedby')
77   })
78
79   QUnit.test('should assign a unique id tooltip element', function (assert) {
80     assert.expect(2)
81     $('<a href="#" rel="tooltip" title="Another tooltip"/>')
82       .appendTo('#qunit-fixture')
83       .bootstrapTooltip('show')
84
85     var id = $('.tooltip').attr('id')
86
87     assert.strictEqual($('#' + id).length, 1, 'tooltip has unique id')
88     assert.strictEqual(id.indexOf('tooltip'), 0, 'tooltip id has prefix')
89   })
90
91   QUnit.test('should place tooltips relative to placement option', function (assert) {
92     assert.expect(2)
93     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
94       .appendTo('#qunit-fixture')
95       .bootstrapTooltip({ placement: 'bottom' })
96
97     $tooltip.bootstrapTooltip('show')
98     assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
99
100     $tooltip.bootstrapTooltip('hide')
101     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
102   })
103
104   QUnit.test('should allow html entities', function (assert) {
105     assert.expect(2)
106     var $tooltip = $('<a href="#" rel="tooltip" title="&lt;b&gt;@fat&lt;/b&gt;"/>')
107       .appendTo('#qunit-fixture')
108       .bootstrapTooltip({ html: true })
109
110     $tooltip.bootstrapTooltip('show')
111     assert.notEqual($('.tooltip b').length, 0, 'b tag was inserted')
112
113     $tooltip.bootstrapTooltip('hide')
114     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
115   })
116
117   QUnit.test('should respect custom classes', function (assert) {
118     assert.expect(2)
119     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
120       .appendTo('#qunit-fixture')
121       .bootstrapTooltip({ template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>' })
122
123     $tooltip.bootstrapTooltip('show')
124     assert.ok($('.tooltip').hasClass('some-class'), 'custom class is present')
125
126     $tooltip.bootstrapTooltip('hide')
127     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed')
128   })
129
130   QUnit.test('should fire show event', function (assert) {
131     assert.expect(1)
132     var done = assert.async()
133
134     $('<div title="tooltip title"/>')
135       .on('show.bs.tooltip', function () {
136         assert.ok(true, 'show event fired')
137         done()
138       })
139       .bootstrapTooltip('show')
140   })
141
142   QUnit.test('should fire shown event', function (assert) {
143     assert.expect(1)
144     var done = assert.async()
145
146     $('<div title="tooltip title"></div>')
147       .appendTo('#qunit-fixture')
148       .on('shown.bs.tooltip', function () {
149         assert.ok(true, 'shown was called')
150         done()
151       })
152       .bootstrapTooltip('show')
153   })
154
155   QUnit.test('should not fire shown event when show was prevented', function (assert) {
156     assert.expect(1)
157     var done = assert.async()
158
159     $('<div title="tooltip title"/>')
160       .on('show.bs.tooltip', function (e) {
161         e.preventDefault()
162         assert.ok(true, 'show event fired')
163         done()
164       })
165       .on('shown.bs.tooltip', function () {
166         assert.ok(false, 'shown event fired')
167       })
168       .bootstrapTooltip('show')
169   })
170
171   QUnit.test('should fire hide event', function (assert) {
172     assert.expect(1)
173     var done = assert.async()
174
175     $('<div title="tooltip title"/>')
176       .appendTo('#qunit-fixture')
177       .on('shown.bs.tooltip', function () {
178         $(this).bootstrapTooltip('hide')
179       })
180       .on('hide.bs.tooltip', function () {
181         assert.ok(true, 'hide event fired')
182         done()
183       })
184       .bootstrapTooltip('show')
185   })
186
187   QUnit.test('should fire hidden event', function (assert) {
188     assert.expect(1)
189     var done = assert.async()
190
191     $('<div title="tooltip title"/>')
192       .appendTo('#qunit-fixture')
193       .on('shown.bs.tooltip', function () {
194         $(this).bootstrapTooltip('hide')
195       })
196       .on('hidden.bs.tooltip', function () {
197         assert.ok(true, 'hidden event fired')
198         done()
199       })
200       .bootstrapTooltip('show')
201   })
202
203   QUnit.test('should not fire hidden event when hide was prevented', function (assert) {
204     assert.expect(1)
205     var done = assert.async()
206
207     $('<div title="tooltip title"/>')
208       .appendTo('#qunit-fixture')
209       .on('shown.bs.tooltip', function () {
210         $(this).bootstrapTooltip('hide')
211       })
212       .on('hide.bs.tooltip', function (e) {
213         e.preventDefault()
214         assert.ok(true, 'hide event fired')
215         done()
216       })
217       .on('hidden.bs.tooltip', function () {
218         assert.ok(false, 'hidden event fired')
219       })
220       .bootstrapTooltip('show')
221   })
222
223   QUnit.test('should destroy tooltip', function (assert) {
224     assert.expect(7)
225     var $tooltip = $('<div/>')
226       .bootstrapTooltip()
227       .on('click.foo', function () {})
228
229     assert.ok($tooltip.data('bs.tooltip'), 'tooltip has data')
230     assert.ok($._data($tooltip[0], 'events').mouseover && $._data($tooltip[0], 'events').mouseout, 'tooltip has hover events')
231     assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip has extra click.foo event')
232
233     $tooltip.bootstrapTooltip('show')
234     $tooltip.bootstrapTooltip('destroy')
235
236     assert.ok(!$tooltip.hasClass('in'), 'tooltip is hidden')
237     assert.ok(!$._data($tooltip[0], 'bs.tooltip'), 'tooltip does not have data')
238     assert.strictEqual($._data($tooltip[0], 'events').click[0].namespace, 'foo', 'tooltip still has click.foo')
239     assert.ok(!$._data($tooltip[0], 'events').mouseover && !$._data($tooltip[0], 'events').mouseout, 'tooltip does not have hover events')
240   })
241
242   QUnit.test('should show tooltip with delegate selector on click', function (assert) {
243     assert.expect(2)
244     var $div = $('<div><a href="#" rel="tooltip" title="Another tooltip"/></div>')
245       .appendTo('#qunit-fixture')
246       .bootstrapTooltip({
247         selector: 'a[rel="tooltip"]',
248         trigger: 'click'
249       })
250
251     $div.find('a').trigger('click')
252     assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
253
254     $div.find('a').trigger('click')
255     assert.strictEqual($('.tooltip').length, 0, 'tooltip was removed from dom')
256   })
257
258   QUnit.test('should show tooltip when toggle is called', function (assert) {
259     assert.expect(1)
260     $('<a href="#" rel="tooltip" title="tooltip on toggle"/>')
261       .appendTo('#qunit-fixture')
262       .bootstrapTooltip({ trigger: 'manual' })
263       .bootstrapTooltip('toggle')
264
265     assert.ok($('.tooltip').is('.fade.in'), 'tooltip is faded in')
266   })
267
268   QUnit.test('should hide previously shown tooltip when toggle is called on tooltip', function (assert) {
269     assert.expect(1)
270     $('<a href="#" rel="tooltip" title="tooltip on toggle">@ResentedHook</a>')
271       .appendTo('#qunit-fixture')
272       .bootstrapTooltip({ trigger: 'manual' })
273       .bootstrapTooltip('show')
274
275     $('.tooltip').bootstrapTooltip('toggle')
276     assert.ok($('.tooltip').not('.fade.in'), 'tooltip was faded out')
277   })
278
279   QUnit.test('should place tooltips inside body when container is body', function (assert) {
280     assert.expect(3)
281     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
282       .appendTo('#qunit-fixture')
283       .bootstrapTooltip({ container: 'body' })
284       .bootstrapTooltip('show')
285
286     assert.notEqual($('body > .tooltip').length, 0, 'tooltip is direct descendant of body')
287     assert.strictEqual($('#qunit-fixture > .tooltip').length, 0, 'tooltip is not in parent')
288
289     $tooltip.bootstrapTooltip('hide')
290     assert.strictEqual($('body > .tooltip').length, 0, 'tooltip was removed from dom')
291   })
292
293   QUnit.test('should add position class before positioning so that position-specific styles are taken into account', function (assert) {
294     assert.expect(1)
295     var styles = '<style>'
296         + '.tooltip.right { white-space: nowrap; }'
297         + '.tooltip.right .tooltip-inner { max-width: none; }'
298         + '</style>'
299     var $styles = $(styles).appendTo('head')
300
301     var $container = $('<div/>').appendTo('#qunit-fixture')
302     var $target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"/>')
303       .appendTo($container)
304       .bootstrapTooltip({
305         placement: 'right',
306         viewport: null
307       })
308       .bootstrapTooltip('show')
309     var $tooltip = $container.find('.tooltip')
310
311     // this is some dumb hack shit because sub pixels in firefox
312     var top = Math.round($target.offset().top + ($target[0].offsetHeight / 2) - ($tooltip[0].offsetHeight / 2))
313     var top2 = Math.round($tooltip.offset().top)
314     var topDiff = top - top2
315     assert.ok(topDiff <= 1 && topDiff >= -1)
316     $target.bootstrapTooltip('hide')
317
318     $container.remove()
319     $styles.remove()
320   })
321
322   QUnit.test('should use title attribute for tooltip text', function (assert) {
323     assert.expect(2)
324     var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
325       .appendTo('#qunit-fixture')
326       .bootstrapTooltip()
327
328     $tooltip.bootstrapTooltip('show')
329     assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set')
330
331     $tooltip.bootstrapTooltip('hide')
332     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
333   })
334
335   QUnit.test('should prefer title attribute over title option', function (assert) {
336     assert.expect(2)
337     var $tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip"/>')
338       .appendTo('#qunit-fixture')
339       .bootstrapTooltip({
340         title: 'This is a tooltip with some content'
341       })
342
343     $tooltip.bootstrapTooltip('show')
344     assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while preferred over title option')
345
346     $tooltip.bootstrapTooltip('hide')
347     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
348   })
349
350   QUnit.test('should use title option', function (assert) {
351     assert.expect(2)
352     var $tooltip = $('<a href="#" rel="tooltip"/>')
353       .appendTo('#qunit-fixture')
354       .bootstrapTooltip({
355         title: 'This is a tooltip with some content'
356       })
357
358     $tooltip.bootstrapTooltip('show')
359     assert.strictEqual($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set')
360
361     $tooltip.bootstrapTooltip('hide')
362     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
363   })
364
365   QUnit.test('should be placed dynamically with the dynamic placement option', function (assert) {
366     assert.expect(6)
367     var $style = $('<style> a[rel="tooltip"] { display: inline-block; position: absolute; } </style>')
368     var $container = $('<div/>')
369       .css({
370         position: 'absolute',
371         overflow: 'hidden',
372         width: 600,
373         height: 400,
374         top: 0,
375         left: 0
376       })
377       .appendTo(document.body)
378
379     var $topTooltip = $('<div style="left: 0; top: 0;" rel="tooltip" title="Top tooltip">Top Dynamic Tooltip</div>')
380       .appendTo($container)
381       .bootstrapTooltip({ placement: 'auto' })
382
383     $topTooltip.bootstrapTooltip('show')
384     assert.ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom')
385
386     $topTooltip.bootstrapTooltip('hide')
387     assert.strictEqual($('.tooltip').length, 0, 'top positioned tooltip removed from dom')
388
389     var $rightTooltip = $('<div style="right: 0;" rel="tooltip" title="Right tooltip">Right Dynamic Tooltip</div>')
390       .appendTo($container)
391       .bootstrapTooltip({ placement: 'right auto' })
392
393     $rightTooltip.bootstrapTooltip('show')
394     assert.ok($('.tooltip').is('.left'), 'right positioned tooltip is dynamically positioned left')
395
396     $rightTooltip.bootstrapTooltip('hide')
397     assert.strictEqual($('.tooltip').length, 0, 'right positioned tooltip removed from dom')
398
399     var $leftTooltip = $('<div style="left: 0;" rel="tooltip" title="Left tooltip">Left Dynamic Tooltip</div>')
400       .appendTo($container)
401       .bootstrapTooltip({ placement: 'auto left' })
402
403     $leftTooltip.bootstrapTooltip('show')
404     assert.ok($('.tooltip').is('.right'), 'left positioned tooltip is dynamically positioned right')
405
406     $leftTooltip.bootstrapTooltip('hide')
407     assert.strictEqual($('.tooltip').length, 0, 'left positioned tooltip removed from dom')
408
409     $container.remove()
410     $style.remove()
411   })
412
413   QUnit.test('should position tip on top if viewport has enough space and placement is "auto top"', function (assert) {
414     assert.expect(2)
415     var styles = '<style>'
416         + 'body { padding-top: 100px; }'
417         + '#section { height: 300px; border: 1px solid red; padding-top: 50px }'
418         + 'div[rel="tooltip"] { width: 150px; border: 1px solid blue; }'
419         + '</style>'
420     var $styles = $(styles).appendTo('head')
421
422     var $container = $('<div id="section"/>').appendTo('#qunit-fixture')
423     var $target = $('<div rel="tooltip" title="tip"/>')
424       .appendTo($container)
425       .bootstrapTooltip({
426         placement: 'auto top',
427         viewport: '#section'
428       })
429
430     $target.bootstrapTooltip('show')
431     assert.ok($('.tooltip').is('.top'), 'top positioned tooltip is dynamically positioned to top')
432
433     $target.bootstrapTooltip('hide')
434     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
435
436     $styles.remove()
437   })
438
439   QUnit.test('should position tip on bottom if the tip\'s dimension exceeds the viewport area and placement is "auto top"', function (assert) {
440     assert.expect(2)
441     var styles = '<style>'
442         + 'body { padding-top: 100px; }'
443         + '#section { height: 300px; border: 1px solid red; }'
444         + 'div[rel="tooltip"] { width: 150px; border: 1px solid blue; }'
445         + '</style>'
446     var $styles = $(styles).appendTo('head')
447
448     var $container = $('<div id="section"/>').appendTo('#qunit-fixture')
449     var $target = $('<div rel="tooltip" title="tip"/>')
450       .appendTo($container)
451       .bootstrapTooltip({
452         placement: 'auto top',
453         viewport: '#section'
454       })
455
456     $target.bootstrapTooltip('show')
457     assert.ok($('.tooltip').is('.bottom'), 'top positioned tooltip is dynamically positioned to bottom')
458
459     $target.bootstrapTooltip('hide')
460     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
461
462     $styles.remove()
463   })
464
465   QUnit.test('should display the tip on top whenever scrollable viewport has enough room if the given placement is "auto top"', function (assert) {
466     assert.expect(2)
467     var styles = '<style>'
468         + '#scrollable-div { height: 200px; overflow: auto; }'
469         + '.tooltip-item { margin: 200px 0 400px; width: 150px; }'
470         + '</style>'
471     var $styles = $(styles).appendTo('head')
472
473     var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
474     var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
475       .appendTo($container)
476       .bootstrapTooltip({
477         placement: 'top auto',
478         viewport: '#scrollable-div'
479       })
480
481     $('#scrollable-div').scrollTop(100)
482
483     $target.bootstrapTooltip('show')
484     assert.ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
485
486     $target.bootstrapTooltip('hide')
487     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
488
489     $styles.remove()
490   })
491
492   QUnit.test('should display the tip on bottom whenever scrollable viewport doesn\'t have enough room if the given placement is "auto top"', function (assert) {
493     assert.expect(2)
494     var styles = '<style>'
495         + '#scrollable-div { height: 200px; overflow: auto; }'
496         + '.tooltip-item { padding: 200px 0 400px; width: 150px; }'
497         + '</style>'
498     var $styles = $(styles).appendTo('head')
499
500     var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
501     var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
502       .appendTo($container)
503       .bootstrapTooltip({
504         placement: 'top auto',
505         viewport: '#scrollable-div'
506       })
507
508     $('#scrollable-div').scrollTop(200)
509
510     $target.bootstrapTooltip('show')
511     assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
512
513     $target.bootstrapTooltip('hide')
514     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
515
516     $styles.remove()
517   })
518
519   QUnit.test('should display the tip on bottom whenever scrollable viewport has enough room if the given placement is "auto bottom"', function (assert) {
520     assert.expect(2)
521     var styles = '<style>'
522         + '#scrollable-div { height: 200px; overflow: auto; }'
523         + '.spacer { height: 400px; }'
524         + '.spacer:first-child { height: 200px; }'
525         + '.tooltip-item { width: 150px; }'
526         + '</style>'
527     var $styles = $(styles).appendTo('head')
528
529     var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
530     var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
531       .appendTo($container)
532       .before('<div class="spacer"/>')
533       .after('<div class="spacer"/>')
534       .bootstrapTooltip({
535         placement: 'bottom auto',
536         viewport: '#scrollable-div'
537       })
538
539     $('#scrollable-div').scrollTop(200)
540
541     $target.bootstrapTooltip('show')
542     assert.ok($('.tooltip').is('.fade.bottom.in'), 'has correct classes applied')
543
544     $target.bootstrapTooltip('hide')
545     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
546
547     $styles.remove()
548   })
549
550   QUnit.test('should display the tip on top whenever scrollable viewport doesn\'t have enough room if the given placement is "auto bottom"', function (assert) {
551     assert.expect(2)
552     var styles = '<style>'
553         + '#scrollable-div { height: 200px; overflow: auto; }'
554         + '.tooltip-item { margin-top: 400px; width: 150px; }'
555         + '</style>'
556     var $styles = $(styles).appendTo('head')
557
558     var $container = $('<div id="scrollable-div"/>').appendTo('#qunit-fixture')
559     var $target = $('<div rel="tooltip" title="tip" class="tooltip-item">Tooltip Item</div>')
560       .appendTo($container)
561       .bootstrapTooltip({
562         placement: 'bottom auto',
563         viewport: '#scrollable-div'
564       })
565
566     $('#scrollable-div').scrollTop(400)
567
568     $target.bootstrapTooltip('show')
569     assert.ok($('.tooltip').is('.fade.top.in'), 'has correct classes applied')
570
571     $target.bootstrapTooltip('hide')
572     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
573
574     $styles.remove()
575   })
576
577   QUnit.test('should adjust the tip\'s top position when up against the top of the viewport', function (assert) {
578     assert.expect(2)
579     var styles = '<style>'
580         + '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
581         + 'a[rel="tooltip"] { position: fixed; }'
582         + '</style>'
583     var $styles = $(styles).appendTo('head')
584
585     var $container = $('<div/>').appendTo('#qunit-fixture')
586     var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>')
587       .appendTo($container)
588       .bootstrapTooltip({
589         placement: 'right',
590         viewport: {
591           selector: 'body',
592           padding: 12
593         }
594       })
595
596     $target.bootstrapTooltip('show')
597     assert.strictEqual(Math.round($container.find('.tooltip').offset().top), 12)
598
599     $target.bootstrapTooltip('hide')
600     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
601
602     $styles.remove()
603   })
604
605   QUnit.test('should adjust the tip\'s top position when up against the bottom of the viewport', function (assert) {
606     assert.expect(2)
607     var styles = '<style>'
608         + '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
609         + 'a[rel="tooltip"] { position: fixed; }'
610         + '</style>'
611     var $styles = $(styles).appendTo('head')
612
613     var $container = $('<div/>').appendTo('#qunit-fixture')
614     var $target = $('<a href="#" rel="tooltip" title="tip" style="bottom: 0px; left: 0px;"/>')
615       .appendTo($container)
616       .bootstrapTooltip({
617         placement: 'right',
618         viewport: {
619           selector: 'body',
620           padding: 12
621         }
622       })
623
624     $target.bootstrapTooltip('show')
625     var $tooltip = $container.find('.tooltip')
626     assert.strictEqual(Math.round($tooltip.offset().top), Math.round($(window).height() - 12 - $tooltip[0].offsetHeight))
627
628     $target.bootstrapTooltip('hide')
629     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
630
631     $container.remove()
632     $styles.remove()
633   })
634
635   QUnit.test('should adjust the tip\'s left position when up against the left of the viewport', function (assert) {
636     assert.expect(2)
637     var styles = '<style>'
638         + '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
639         + 'a[rel="tooltip"] { position: fixed; }'
640         + '</style>'
641     var $styles = $(styles).appendTo('head')
642
643     var $container = $('<div/>').appendTo('#qunit-fixture')
644     var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; left: 0px;"/>')
645       .appendTo($container)
646       .bootstrapTooltip({
647         placement: 'bottom',
648         viewport: {
649           selector: 'body',
650           padding: 12
651         }
652       })
653
654     $target.bootstrapTooltip('show')
655     assert.strictEqual(Math.round($container.find('.tooltip').offset().left), 12)
656
657     $target.bootstrapTooltip('hide')
658     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
659
660     $container.remove()
661     $styles.remove()
662   })
663
664   QUnit.test('should adjust the tip\'s left position when up against the right of the viewport', function (assert) {
665     assert.expect(2)
666     var styles = '<style>'
667         + '.tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
668         + 'a[rel="tooltip"] { position: fixed; }'
669         + '</style>'
670     var $styles = $(styles).appendTo('head')
671
672     var $container = $('<div/>').appendTo('body')
673     var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 0px; right: 0px;"/>')
674       .appendTo($container)
675       .bootstrapTooltip({
676         placement: 'bottom',
677         viewport: {
678           selector: 'body',
679           padding: 12
680         }
681       })
682
683     $target.bootstrapTooltip('show')
684     var $tooltip = $container.find('.tooltip')
685     assert.strictEqual(Math.round($tooltip.offset().left), Math.round($(window).width() - 12 - $tooltip[0].offsetWidth))
686
687     $target.bootstrapTooltip('hide')
688     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
689
690     $container.remove()
691     $styles.remove()
692   })
693
694   QUnit.test('should adjust the tip when up against the right of an arbitrary viewport', function (assert) {
695     assert.expect(2)
696     var styles = '<style>'
697         + '.tooltip, .tooltip .tooltip-inner { width: 200px; height: 200px; max-width: none; }'
698         + '.container-viewport { position: absolute; top: 50px; left: 60px; width: 300px; height: 300px; }'
699         + 'a[rel="tooltip"] { position: fixed; }'
700         + '</style>'
701     var $styles = $(styles).appendTo('head')
702
703     var $container = $('<div class="container-viewport"/>').appendTo(document.body)
704     var $target = $('<a href="#" rel="tooltip" title="tip" style="top: 50px; left: 350px;"/>')
705       .appendTo($container)
706       .bootstrapTooltip({
707         placement: 'bottom',
708         viewport: '.container-viewport'
709       })
710
711     $target.bootstrapTooltip('show')
712     var $tooltip = $container.find('.tooltip')
713     assert.strictEqual(Math.round($tooltip.offset().left), Math.round(60 + $container.width() - $tooltip[0].offsetWidth))
714
715     $target.bootstrapTooltip('hide')
716     assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
717
718     $container.remove()
719     $styles.remove()
720   })
721
722   QUnit.test('should not error when trying to show an auto-placed tooltip that has been removed from the dom', function (assert) {
723     assert.expect(1)
724     var passed = true
725     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
726       .appendTo('#qunit-fixture')
727       .one('show.bs.tooltip', function () {
728         $(this).remove()
729       })
730       .bootstrapTooltip({ placement: 'auto' })
731
732     try {
733       $tooltip.bootstrapTooltip('show')
734     } catch (err) {
735       passed = false
736       console.log(err)
737     }
738
739     assert.ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom')
740   })
741
742   QUnit.test('should place tooltip on top of element', function (assert) {
743     assert.expect(1)
744     var done = assert.async()
745
746     var containerHTML = '<div>'
747         + '<p style="margin-top: 200px">'
748         + '<a href="#" title="very very very very very very very long tooltip">Hover me</a>'
749         + '</p>'
750         + '</div>'
751
752     var $container = $(containerHTML)
753       .css({
754         position: 'absolute',
755         bottom: 0,
756         left: 0,
757         textAlign: 'right',
758         width: 300,
759         height: 300
760       })
761       .appendTo('#qunit-fixture')
762
763     var $trigger = $container
764       .find('a')
765       .css('margin-top', 200)
766       .bootstrapTooltip({
767         placement: 'top',
768         animate: false
769       })
770       .bootstrapTooltip('show')
771
772     var $tooltip = $container.find('.tooltip')
773
774     setTimeout(function () {
775       assert.ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top))
776       done()
777     }, 0)
778   })
779
780   QUnit.test('should place tooltip inside viewport', function (assert) {
781     assert.expect(1)
782     var done = assert.async()
783
784     var $container = $('<div/>')
785       .css({
786         position: 'absolute',
787         width: 200,
788         height: 200,
789         bottom: 0,
790         left: 0
791       })
792       .appendTo('#qunit-fixture')
793
794     $('<a href="#" title="Very very very very very very very very long tooltip">Hover me</a>')
795       .css({
796         position: 'absolute',
797         top: 0,
798         left: 0
799       })
800       .appendTo($container)
801       .bootstrapTooltip({
802         placement: 'top'
803       })
804       .bootstrapTooltip('show')
805
806     setTimeout(function () {
807       assert.ok($('.tooltip').offset().left >= 0)
808       done()
809     }, 0)
810   })
811
812   QUnit.test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
813     assert.expect(2)
814     var done = assert.async()
815
816     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
817       .appendTo('#qunit-fixture')
818       .bootstrapTooltip({ delay: 150 })
819
820     setTimeout(function () {
821       assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip is not faded in')
822     }, 100)
823
824     setTimeout(function () {
825       assert.ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in')
826       done()
827     }, 200)
828
829     $tooltip.trigger('mouseenter')
830   })
831
832   QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
833     assert.expect(2)
834     var done = assert.async()
835
836     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
837       .appendTo('#qunit-fixture')
838       .bootstrapTooltip({ delay: 150 })
839
840     setTimeout(function () {
841       assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
842       $tooltip.trigger('mouseout')
843     }, 100)
844
845     setTimeout(function () {
846       assert.ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
847       done()
848     }, 200)
849
850     $tooltip.trigger('mouseenter')
851   })
852
853   QUnit.test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
854     assert.expect(3)
855     var done = assert.async()
856
857     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
858       .appendTo('#qunit-fixture')
859       .bootstrapTooltip({ delay: { show: 0, hide: 150 }})
860
861     setTimeout(function () {
862       assert.ok($('.tooltip').is('.fade.in'), '1ms: tooltip faded in')
863       $tooltip.trigger('mouseout')
864
865       setTimeout(function () {
866         assert.ok($('.tooltip').is('.fade.in'), '100ms: tooltip still faded in')
867         $tooltip.trigger('mouseenter')
868       }, 100)
869
870       setTimeout(function () {
871         assert.ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in')
872         done()
873       }, 200)
874     }, 0)
875
876     $tooltip.trigger('mouseenter')
877   })
878
879   QUnit.test('should not show tooltip if leave event occurs before delay expires', function (assert) {
880     assert.expect(2)
881     var done = assert.async()
882
883     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
884       .appendTo('#qunit-fixture')
885       .bootstrapTooltip({ delay: 150 })
886
887     setTimeout(function () {
888       assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
889       $tooltip.trigger('mouseout')
890     }, 100)
891
892     setTimeout(function () {
893       assert.ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
894       done()
895     }, 200)
896
897     $tooltip.trigger('mouseenter')
898   })
899
900   QUnit.test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
901     assert.expect(2)
902     var done = assert.async()
903
904     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
905       .appendTo('#qunit-fixture')
906       .bootstrapTooltip({ delay: { show: 150, hide: 0 }})
907
908     setTimeout(function () {
909       assert.ok(!$('.tooltip').is('.fade.in'), '100ms: tooltip not faded in')
910       $tooltip.trigger('mouseout')
911     }, 100)
912
913     setTimeout(function () {
914       assert.ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in')
915       done()
916     }, 250)
917
918     $tooltip.trigger('mouseenter')
919   })
920
921   QUnit.test('should wait 200ms before hiding the tooltip', function (assert) {
922     assert.expect(3)
923     var done = assert.async()
924
925     var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
926       .appendTo('#qunit-fixture')
927       .bootstrapTooltip({ delay: { show: 0, hide: 150 }})
928
929     setTimeout(function () {
930       assert.ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '1ms: tooltip faded in')
931
932       $tooltip.trigger('mouseout')
933
934       setTimeout(function () {
935         assert.ok($tooltip.data('bs.tooltip').$tip.is('.fade.in'), '100ms: tooltip still faded in')
936       }, 100)
937
938       setTimeout(function () {
939         assert.ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
940         done()
941       }, 200)
942
943     }, 0)
944
945     $tooltip.trigger('mouseenter')
946   })
947
948   QUnit.test('should correctly position tooltips on SVG elements', function (assert) {
949     if (!window.SVGElement) {
950       // Skip IE8 since it doesn't support SVG
951       assert.expect(0)
952       return
953     }
954     assert.expect(2)
955
956     var done = assert.async()
957
958     var styles = '<style>'
959         + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
960         + '.tooltip { position: absolute; }'
961         + '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }'
962         + '</style>'
963     var $styles = $(styles).appendTo('head')
964
965     $('#qunit-fixture').append(
966         '<div style="position: fixed; top: 0; left: 0;">'
967       + '  <svg width="200" height="200">'
968       + '    <circle cx="100" cy="100" r="10" title="m" id="theCircle" />'
969       + '  </svg>'
970       + '</div>')
971     var $circle = $('#theCircle')
972
973     $circle
974       .on('shown.bs.tooltip', function () {
975         var offset = $('.tooltip').offset()
976         $styles.remove()
977         assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
978         $circle.bootstrapTooltip('hide')
979         assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
980         done()
981       })
982       .bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' })
983
984     $circle.bootstrapTooltip('show')
985   })
986
987   QUnit.test('should correctly determine auto placement based on container rather than parent', function (assert) {
988     assert.expect(2)
989     var done = assert.async()
990
991     var styles = '<style>'
992         + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
993         + '.tooltip { position: absolute; display: block; font-size: 12px; line-height: 1.4; }'
994         + '.tooltip .tooltip-inner { max-width: 200px; padding: 3px 8px; font-family: Helvetica; text-align: center; }'
995         + '#trigger-parent {'
996         + '  position: fixed;'
997         + '  top: 100px;'
998         + '  right: 17px;'
999         + '}'
1000         + '</style>'
1001     var $styles = $(styles).appendTo('head')
1002
1003     $('#qunit-fixture').append('<span id="trigger-parent"><a id="tt-trigger" title="If a_larger_text is written here, it won\'t fit using older broken version of BS">HOVER OVER ME</a></span>')
1004     var $trigger = $('#tt-trigger')
1005
1006     $trigger
1007       .on('shown.bs.tooltip', function () {
1008         var $tip = $('.tooltip-inner')
1009         var tipXrightEdge = $tip.offset().left + $tip.width()
1010         var triggerXleftEdge = $trigger.offset().left
1011         assert.ok(tipXrightEdge < triggerXleftEdge, 'tooltip with auto left placement, when near the right edge of the viewport, gets left placement')
1012         $trigger.bootstrapTooltip('hide')
1013       })
1014       .on('hidden.bs.tooltip', function () {
1015         $styles.remove()
1016         $(this).remove()
1017         assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
1018         done()
1019       })
1020       .bootstrapTooltip({
1021         container: 'body',
1022         placement: 'auto left',
1023         trigger: 'manual'
1024       })
1025
1026     $trigger.bootstrapTooltip('show')
1027   })
1028
1029   QUnit.test('should not reload the tooltip on subsequent mouseenter events', function (assert) {
1030     assert.expect(1)
1031     var titleHtml = function () {
1032       var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
1033       return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
1034     }
1035
1036     var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>')
1037       .appendTo('#qunit-fixture')
1038
1039     $tooltip.bootstrapTooltip({
1040       html: true,
1041       animation: false,
1042       trigger: 'hover',
1043       delay: { show: 0, hide: 500 },
1044       container: $tooltip,
1045       title: titleHtml
1046     })
1047
1048     $('#tt-outer').trigger('mouseenter')
1049
1050     var currentUid = $('#tt-content').text()
1051
1052     $('#tt-content').trigger('mouseenter')
1053     assert.strictEqual(currentUid, $('#tt-content').text())
1054   })
1055
1056   QUnit.test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function (assert) {
1057     assert.expect(4)
1058     var titleHtml = function () {
1059       var uid = $.fn.bootstrapTooltip.Constructor.prototype.getUID('tooltip')
1060       return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
1061     }
1062
1063     var $tooltip = $('<span id="tt-outer" rel="tooltip" data-trigger="hover" data-placement="top">some text</span>')
1064       .appendTo('#qunit-fixture')
1065
1066     $tooltip.bootstrapTooltip({
1067       html: true,
1068       animation: false,
1069       trigger: 'hover',
1070       delay: { show: 0, hide: 500 },
1071       container: $tooltip,
1072       title: titleHtml
1073     })
1074
1075     var obj = $tooltip.data('bs.tooltip')
1076
1077     $('#tt-outer').trigger('mouseenter')
1078
1079     var currentUid = $('#tt-content').text()
1080
1081     $('#tt-outer').trigger('mouseleave')
1082     assert.strictEqual(currentUid, $('#tt-content').text())
1083
1084     assert.ok(obj.hoverState == 'out', 'the tooltip hoverState should be set to "out"')
1085
1086     $('#tt-content').trigger('mouseenter')
1087     assert.ok(obj.hoverState == 'in', 'the tooltip hoverState should be set to "in"')
1088
1089     assert.strictEqual(currentUid, $('#tt-content').text())
1090   })
1091
1092   QUnit.test('should position arrow correctly when tooltip is moved to not appear offscreen', function (assert) {
1093     assert.expect(2)
1094     var done = assert.async()
1095
1096     var styles = '<style>'
1097         + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
1098         + '.tooltip { position: absolute; }'
1099         + '.tooltip-arrow { position: absolute; width: 0; height: 0; }'
1100         + '.tooltip .tooltip-inner { max-width: 200px; padding: 3px 8px; }'
1101         + '</style>'
1102     var $styles = $(styles).appendTo('head')
1103
1104     $('<a href="#" title="tooltip title" style="position: absolute; bottom: 0; right: 0;">Foobar</a>')
1105       .appendTo('body')
1106       .on('shown.bs.tooltip', function () {
1107         var arrowStyles = $(this).data('bs.tooltip').$tip.find('.tooltip-arrow').attr('style')
1108         assert.ok(/left/i.test(arrowStyles) && !/top/i.test(arrowStyles), 'arrow positioned correctly')
1109         $(this).bootstrapTooltip('hide')
1110       })
1111       .on('hidden.bs.tooltip', function () {
1112         $styles.remove()
1113         $(this).remove()
1114         assert.strictEqual($('.tooltip').length, 0, 'tooltip removed from dom')
1115         done()
1116       })
1117       .bootstrapTooltip({
1118         container: 'body',
1119         placement: 'top',
1120         trigger: 'manual'
1121       })
1122       .bootstrapTooltip('show')
1123   })
1124
1125   QUnit.test('should correctly position tooltips on transformed elements', function (assert) {
1126     var styleProps = document.documentElement.style
1127     if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) {
1128       assert.expect(0)
1129       return
1130     }
1131     assert.expect(2)
1132
1133     var done = assert.async()
1134
1135     var styles = '<style>'
1136         + '#qunit-fixture { top: 0; left: 0; }'
1137         + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
1138         + '.tooltip { position: absolute; }'
1139         + '.tooltip .tooltip-inner { width: 24px; height: 24px; font-family: Helvetica; }'
1140         + '#target { position: absolute; top: 100px; left: 50px; width: 100px; height: 200px; -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); }'
1141         + '</style>'
1142     var $styles = $(styles).appendTo('head')
1143
1144     var $element = $('<div id="target" title="1"/>').appendTo('#qunit-fixture')
1145
1146     $element
1147       .on('shown.bs.tooltip', function () {
1148         var offset = $('.tooltip').offset()
1149         $styles.remove()
1150         assert.ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
1151         assert.ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
1152         $element.bootstrapTooltip('hide')
1153         done()
1154       })
1155       .bootstrapTooltip({
1156         container: 'body',
1157         placement: 'top',
1158         trigger: 'manual'
1159       })
1160
1161     $element.bootstrapTooltip('show')
1162   })
1163
1164   QUnit.test('should throw an error when initializing tooltip on the document object without specifying a delegation selector', function (assert) {
1165     assert.expect(1)
1166     assert.throws(function () {
1167       $(document).bootstrapTooltip({ title: 'What am I on?' })
1168     }, new Error('`selector` option must be specified when initializing tooltip on the window.document object!'))
1169   })
1170
1171   QUnit.test('should do nothing when an attempt is made to hide an uninitialized tooltip', function (assert) {
1172     assert.expect(1)
1173
1174     var $tooltip = $('<span data-toggle="tooltip" title="some tip">some text</span>')
1175       .appendTo('#qunit-fixture')
1176       .on('hidden.bs.tooltip shown.bs.tooltip', function () {
1177         assert.ok(false, 'should not fire any tooltip events')
1178       })
1179       .bootstrapTooltip('hide')
1180     assert.strictEqual($tooltip.data('bs.tooltip'), undefined, 'should not initialize the tooltip')
1181   })
1182
1183 })