tooltip.js 47 KB

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