popover.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. $(function () {
  2. 'use strict'
  3. QUnit.module('popover plugin')
  4. QUnit.test('should be defined on jquery object', function (assert) {
  5. assert.expect(1)
  6. assert.ok($(document.body).popover, 'popover method is defined')
  7. })
  8. QUnit.module('popover', {
  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.bootstrapPopover = $.fn.popover.noConflict()
  12. },
  13. afterEach: function () {
  14. $.fn.popover = $.fn.bootstrapPopover
  15. delete $.fn.bootstrapPopover
  16. $('.popover').remove()
  17. $('#qunit-fixture').html('')
  18. }
  19. })
  20. QUnit.test('should provide no conflict', function (assert) {
  21. assert.expect(1)
  22. assert.strictEqual(typeof $.fn.popover, 'undefined', 'popover was set back to undefined (org value)')
  23. })
  24. QUnit.test('should throw explicit error on undefined method', function (assert) {
  25. assert.expect(1)
  26. var $el = $('<div/>')
  27. $el.bootstrapPopover()
  28. try {
  29. $el.bootstrapPopover('noMethod')
  30. } catch (err) {
  31. assert.strictEqual(err.message, 'No method named "noMethod"')
  32. }
  33. })
  34. QUnit.test('should return jquery collection containing the element', function (assert) {
  35. assert.expect(2)
  36. var $el = $('<div/>')
  37. var $popover = $el.bootstrapPopover()
  38. assert.ok($popover instanceof $, 'returns jquery collection')
  39. assert.strictEqual($popover[0], $el[0], 'collection contains element')
  40. })
  41. QUnit.test('should render popover element', function (assert) {
  42. assert.expect(2)
  43. var done = assert.async()
  44. $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>')
  45. .appendTo('#qunit-fixture')
  46. .on('shown.bs.popover', function () {
  47. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  48. $(this).bootstrapPopover('hide')
  49. })
  50. .on('hidden.bs.popover', function () {
  51. assert.strictEqual($('.popover').length, 0, 'popover removed')
  52. done()
  53. })
  54. .bootstrapPopover('show')
  55. })
  56. QUnit.test('should store popover instance in popover data object', function (assert) {
  57. assert.expect(1)
  58. var $popover = $('<a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a>').bootstrapPopover()
  59. assert.ok($popover.data('bs.popover'), 'popover instance exists')
  60. })
  61. QUnit.test('should store popover trigger in popover instance data object', function (assert) {
  62. assert.expect(1)
  63. var $popover = $('<a href="#" title="ResentedHook">@ResentedHook</a>')
  64. .appendTo('#qunit-fixture')
  65. .bootstrapPopover()
  66. $popover.bootstrapPopover('show')
  67. assert.ok($('.popover').data('bs.popover'), 'popover trigger stored in instance data')
  68. })
  69. QUnit.test('should get title and content from options', function (assert) {
  70. assert.expect(4)
  71. var done = assert.async()
  72. var $popover = $('<a href="#">@fat</a>')
  73. .appendTo('#qunit-fixture')
  74. .bootstrapPopover({
  75. title: function () {
  76. return '@fat'
  77. },
  78. content: function () {
  79. return 'loves writing tests (╯°□°)╯︵ ┻━┻'
  80. }
  81. })
  82. $popover
  83. .one('shown.bs.popover', function () {
  84. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  85. assert.strictEqual($('.popover .popover-header').text(), '@fat', 'title correctly inserted')
  86. assert.strictEqual($('.popover .popover-body').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted')
  87. $popover.bootstrapPopover('hide')
  88. })
  89. .one('hidden.bs.popover', function () {
  90. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  91. done()
  92. })
  93. .bootstrapPopover('show')
  94. })
  95. QUnit.test('should allow DOMElement title and content (html: true)', function (assert) {
  96. assert.expect(5)
  97. var title = document.createTextNode('@glebm <3 writing tests')
  98. var content = $('<i>¯\\_(ツ)_/¯</i>').get(0)
  99. var $popover = $('<a href="#" rel="tooltip"/>')
  100. .appendTo('#qunit-fixture')
  101. .bootstrapPopover({
  102. html: true,
  103. title: title,
  104. content: content
  105. })
  106. $popover.bootstrapPopover('show')
  107. assert.notEqual($('.popover').length, 0, 'popover inserted')
  108. assert.strictEqual($('.popover .popover-header').text(), '@glebm <3 writing tests', 'title inserted')
  109. assert.ok($.contains($('.popover').get(0), title), 'title node moved, not copied')
  110. // toLowerCase because IE8 will return <I>...</I>
  111. assert.strictEqual($('.popover .popover-body').html().toLowerCase(), '<i>¯\\_(ツ)_/¯</i>', 'content inserted')
  112. assert.ok($.contains($('.popover').get(0), content), 'content node moved, not copied')
  113. })
  114. QUnit.test('should allow DOMElement title and content (html: false)', function (assert) {
  115. assert.expect(5)
  116. var title = document.createTextNode('@glebm <3 writing tests')
  117. var content = $('<i>¯\\_(ツ)_/¯</i>').get(0)
  118. var $popover = $('<a href="#" rel="tooltip"/>')
  119. .appendTo('#qunit-fixture')
  120. .bootstrapPopover({
  121. title: title,
  122. content: content
  123. })
  124. $popover.bootstrapPopover('show')
  125. assert.notEqual($('.popover').length, 0, 'popover inserted')
  126. assert.strictEqual($('.popover .popover-header').text(), '@glebm <3 writing tests', 'title inserted')
  127. assert.ok(!$.contains($('.popover').get(0), title), 'title node copied, not moved')
  128. assert.strictEqual($('.popover .popover-body').html(), '¯\\_(ツ)_/¯', 'content inserted')
  129. assert.ok(!$.contains($('.popover').get(0), content), 'content node copied, not moved')
  130. })
  131. QUnit.test('should not duplicate HTML object', function (assert) {
  132. assert.expect(6)
  133. var done = assert.async()
  134. var $div = $('<div/>').html('loves writing tests (╯°□°)╯︵ ┻━┻')
  135. var $popover = $('<a href="#">@fat</a>')
  136. .appendTo('#qunit-fixture')
  137. .bootstrapPopover({
  138. html: true,
  139. content: function () {
  140. return $div
  141. }
  142. })
  143. function popoverInserted() {
  144. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  145. assert.equal($('.popover .popover-body').html(), $div[0].outerHTML, 'content correctly inserted')
  146. }
  147. $popover
  148. .one('shown.bs.popover', function () {
  149. popoverInserted()
  150. $popover.one('hidden.bs.popover', function () {
  151. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  152. $popover.one('shown.bs.popover', function () {
  153. popoverInserted()
  154. $popover.one('hidden.bs.popover', function () {
  155. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  156. done()
  157. }).bootstrapPopover('hide')
  158. }).bootstrapPopover('show')
  159. }).bootstrapPopover('hide')
  160. })
  161. .bootstrapPopover('show')
  162. })
  163. QUnit.test('should get title and content from attributes', function (assert) {
  164. assert.expect(4)
  165. var done = assert.async()
  166. var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
  167. .appendTo('#qunit-fixture')
  168. .bootstrapPopover()
  169. .one('shown.bs.popover', function () {
  170. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  171. assert.strictEqual($('.popover .popover-header').text(), '@mdo', 'title correctly inserted')
  172. assert.strictEqual($('.popover .popover-body').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted')
  173. $popover.bootstrapPopover('hide')
  174. })
  175. .one('hidden.bs.popover', function () {
  176. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  177. done()
  178. })
  179. .bootstrapPopover('show')
  180. })
  181. QUnit.test('should get title and content from attributes ignoring options passed via js', function (assert) {
  182. assert.expect(4)
  183. var done = assert.async()
  184. var $popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
  185. .appendTo('#qunit-fixture')
  186. .bootstrapPopover({
  187. title: 'ignored title option',
  188. content: 'ignored content option'
  189. })
  190. .one('shown.bs.popover', function () {
  191. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  192. assert.strictEqual($('.popover .popover-header').text(), '@mdo', 'title correctly inserted')
  193. assert.strictEqual($('.popover .popover-body').text(), 'loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻', 'content correctly inserted')
  194. $popover.bootstrapPopover('hide')
  195. })
  196. .one('hidden.bs.popover', function () {
  197. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  198. done()
  199. })
  200. .bootstrapPopover('show')
  201. })
  202. QUnit.test('should respect custom template', function (assert) {
  203. assert.expect(3)
  204. var done = assert.async()
  205. var $popover = $('<a href="#">@fat</a>')
  206. .appendTo('#qunit-fixture')
  207. .bootstrapPopover({
  208. title: 'Test',
  209. content: 'Test',
  210. template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>'
  211. })
  212. .one('shown.bs.popover', function () {
  213. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  214. assert.ok($('.popover').hasClass('foobar'), 'custom class is present')
  215. $popover.bootstrapPopover('hide')
  216. })
  217. .one('hidden.bs.popover', function () {
  218. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  219. done()
  220. })
  221. .bootstrapPopover('show')
  222. })
  223. QUnit.test('should destroy popover', function (assert) {
  224. assert.expect(7)
  225. var $popover = $('<div/>')
  226. .bootstrapPopover({
  227. trigger: 'hover'
  228. })
  229. .on('click.foo', $.noop)
  230. assert.ok($popover.data('bs.popover'), 'popover has data')
  231. assert.ok($._data($popover[0], 'events').mouseover && $._data($popover[0], 'events').mouseout, 'popover has hover event')
  232. assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover has extra click.foo event')
  233. $popover.bootstrapPopover('show')
  234. $popover.bootstrapPopover('dispose')
  235. assert.ok(!$popover.hasClass('show'), 'popover is hidden')
  236. assert.ok(!$popover.data('popover'), 'popover does not have data')
  237. assert.strictEqual($._data($popover[0], 'events').click[0].namespace, 'foo', 'popover still has click.foo')
  238. assert.ok(!$._data($popover[0], 'events').mouseover && !$._data($popover[0], 'events').mouseout, 'popover does not have any events')
  239. })
  240. QUnit.test('should render popover element using delegated selector', function (assert) {
  241. assert.expect(2)
  242. var done = assert.async()
  243. var $div = $('<div><a href="#" title="mdo" data-content="https://twitter.com/mdo">@mdo</a></div>')
  244. .appendTo('#qunit-fixture')
  245. .bootstrapPopover({
  246. selector: 'a',
  247. trigger: 'click'
  248. })
  249. .one('shown.bs.popover', function () {
  250. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  251. $div.find('a').trigger('click')
  252. })
  253. .one('hidden.bs.popover', function () {
  254. assert.strictEqual($('.popover').length, 0, 'popover was removed')
  255. done()
  256. })
  257. $div.find('a').trigger('click')
  258. })
  259. QUnit.test('should detach popover content rather than removing it so that event handlers are left intact', function (assert) {
  260. assert.expect(1)
  261. var $content = $('<div class="content-with-handler"><a class="btn btn-warning">Button with event handler</a></div>').appendTo('#qunit-fixture')
  262. var handlerCalled = false
  263. $('.content-with-handler .btn').on('click', function () {
  264. handlerCalled = true
  265. })
  266. var $div = $('<div><a href="#">Show popover</a></div>')
  267. .appendTo('#qunit-fixture')
  268. .bootstrapPopover({
  269. html: true,
  270. trigger: 'manual',
  271. container: 'body',
  272. animation: false,
  273. content: function () {
  274. return $content
  275. }
  276. })
  277. var done = assert.async()
  278. $div
  279. .one('shown.bs.popover', function () {
  280. $div
  281. .one('hidden.bs.popover', function () {
  282. $div
  283. .one('shown.bs.popover', function () {
  284. $('.content-with-handler .btn').trigger('click')
  285. assert.ok(handlerCalled, 'content\'s event handler still present')
  286. $div.bootstrapPopover('dispose')
  287. done()
  288. })
  289. .bootstrapPopover('show')
  290. })
  291. .bootstrapPopover('hide')
  292. })
  293. .bootstrapPopover('show')
  294. })
  295. QUnit.test('should do nothing when an attempt is made to hide an uninitialized popover', function (assert) {
  296. assert.expect(1)
  297. var $popover = $('<span data-toggle="popover" data-title="some title" data-content="some content">some text</span>')
  298. .appendTo('#qunit-fixture')
  299. .on('hidden.bs.popover shown.bs.popover', function () {
  300. assert.ok(false, 'should not fire any popover events')
  301. })
  302. .bootstrapPopover('hide')
  303. assert.strictEqual(typeof $popover.data('bs.popover'), 'undefined', 'should not initialize the popover')
  304. })
  305. QUnit.test('should fire inserted event', function (assert) {
  306. assert.expect(2)
  307. var done = assert.async()
  308. $('<a href="#">@Johann-S</a>')
  309. .appendTo('#qunit-fixture')
  310. .on('inserted.bs.popover', function () {
  311. assert.notEqual($('.popover').length, 0, 'popover was inserted')
  312. assert.ok(true, 'inserted event fired')
  313. done()
  314. })
  315. .bootstrapPopover({
  316. title: 'Test',
  317. content: 'Test'
  318. })
  319. .bootstrapPopover('show')
  320. })
  321. QUnit.test('should throw an error when show is called on hidden elements', function (assert) {
  322. assert.expect(1)
  323. var done = assert.async()
  324. try {
  325. $('<div data-toggle="popover" data-title="some title" data-content="@Johann-S" style="display: none"/>').bootstrapPopover('show')
  326. } catch (err) {
  327. assert.strictEqual(err.message, 'Please use show on visible elements')
  328. done()
  329. }
  330. })
  331. QUnit.test('should hide popovers when their containing modal is closed', function (assert) {
  332. assert.expect(1)
  333. var done = assert.async()
  334. var templateHTML = '<div id="modal-test" class="modal">' +
  335. '<div class="modal-dialog" role="document">' +
  336. '<div class="modal-content">' +
  337. '<div class="modal-body">' +
  338. '<button id="popover-test" type="button" class="btn btn-secondary" data-toggle="popover" data-placement="top" data-content="Popover">' +
  339. 'Popover on top' +
  340. '</button>' +
  341. '</div>' +
  342. '</div>' +
  343. '</div>' +
  344. '</div>'
  345. $(templateHTML).appendTo('#qunit-fixture')
  346. $('#popover-test')
  347. .on('shown.bs.popover', function () {
  348. $('#modal-test').modal('hide')
  349. })
  350. .on('hide.bs.popover', function () {
  351. assert.ok(true, 'popover hide')
  352. done()
  353. })
  354. $('#modal-test')
  355. .on('shown.bs.modal', function () {
  356. $('#popover-test').bootstrapPopover('show')
  357. })
  358. .modal('show')
  359. })
  360. QUnit.test('should convert number to string without error for content and title', function (assert) {
  361. assert.expect(2)
  362. var done = assert.async()
  363. var $popover = $('<a href="#">@mdo</a>')
  364. .appendTo('#qunit-fixture')
  365. .bootstrapPopover({
  366. title: 5,
  367. content: 7
  368. })
  369. .on('shown.bs.popover', function () {
  370. assert.strictEqual($('.popover .popover-header').text(), '5')
  371. assert.strictEqual($('.popover .popover-body').text(), '7')
  372. done()
  373. })
  374. $popover.bootstrapPopover('show')
  375. })
  376. QUnit.test('popover should be shown right away after the call of disable/enable', function (assert) {
  377. assert.expect(2)
  378. var done = assert.async()
  379. var $popover = $('<a href="#">@mdo</a>')
  380. .appendTo('#qunit-fixture')
  381. .bootstrapPopover({
  382. title: 'Test popover',
  383. content: 'with disable/enable'
  384. })
  385. .on('shown.bs.popover', function () {
  386. assert.strictEqual($('.popover').hasClass('show'), true)
  387. done()
  388. })
  389. $popover.bootstrapPopover('disable')
  390. $popover.trigger($.Event('click'))
  391. setTimeout(function () {
  392. assert.strictEqual($('.popover').length === 0, true)
  393. $popover.bootstrapPopover('enable')
  394. $popover.trigger($.Event('click'))
  395. }, 200)
  396. })
  397. QUnit.test('popover should call content function only once', function (assert) {
  398. assert.expect(1)
  399. var done = assert.async()
  400. var nbCall = 0
  401. $('<div id="popover" style="display:none">content</div>').appendTo('#qunit-fixture')
  402. var $popover = $('<a href="#">@Johann-S</a>')
  403. .appendTo('#qunit-fixture')
  404. .bootstrapPopover({
  405. content: function () {
  406. nbCall++
  407. return $('#popover').clone().show().get(0)
  408. }
  409. })
  410. .on('shown.bs.popover', function () {
  411. assert.strictEqual(nbCall, 1)
  412. done()
  413. })
  414. $popover.trigger($.Event('click'))
  415. })
  416. })