popover.spec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. import EventHandler from '../../src/dom/event-handler.js'
  2. import Popover from '../../src/popover.js'
  3. import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
  4. describe('Popover', () => {
  5. let fixtureEl
  6. beforeAll(() => {
  7. fixtureEl = getFixture()
  8. })
  9. afterEach(() => {
  10. clearFixture()
  11. const popoverList = document.querySelectorAll('.popover')
  12. for (const popoverEl of popoverList) {
  13. popoverEl.remove()
  14. }
  15. })
  16. describe('VERSION', () => {
  17. it('should return plugin version', () => {
  18. expect(Popover.VERSION).toEqual(jasmine.any(String))
  19. })
  20. })
  21. describe('Default', () => {
  22. it('should return plugin default config', () => {
  23. expect(Popover.Default).toEqual(jasmine.any(Object))
  24. })
  25. })
  26. describe('NAME', () => {
  27. it('should return plugin name', () => {
  28. expect(Popover.NAME).toEqual(jasmine.any(String))
  29. })
  30. })
  31. describe('DATA_KEY', () => {
  32. it('should return plugin data key', () => {
  33. expect(Popover.DATA_KEY).toEqual('bs.popover')
  34. })
  35. })
  36. describe('EVENT_KEY', () => {
  37. it('should return plugin event key', () => {
  38. expect(Popover.EVENT_KEY).toEqual('.bs.popover')
  39. })
  40. })
  41. describe('DefaultType', () => {
  42. it('should return plugin default type', () => {
  43. expect(Popover.DefaultType).toEqual(jasmine.any(Object))
  44. })
  45. })
  46. describe('show', () => {
  47. it('should toggle a popover after show', () => {
  48. return new Promise(resolve => {
  49. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  50. const popoverEl = fixtureEl.querySelector('a')
  51. const popover = new Popover(popoverEl)
  52. popoverEl.addEventListener('shown.bs.popover', () => {
  53. expect(document.querySelector('.popover')).not.toBeNull()
  54. popover.toggle()
  55. })
  56. popoverEl.addEventListener('hidden.bs.popover', () => {
  57. expect(document.querySelector('.popover')).toBeNull()
  58. resolve()
  59. })
  60. popover.show()
  61. })
  62. })
  63. it('should show a popover', () => {
  64. return new Promise(resolve => {
  65. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  66. const popoverEl = fixtureEl.querySelector('a')
  67. const popover = new Popover(popoverEl)
  68. popoverEl.addEventListener('shown.bs.popover', () => {
  69. expect(document.querySelector('.popover')).not.toBeNull()
  70. resolve()
  71. })
  72. popover.show()
  73. })
  74. })
  75. it('should set title and content from functions', () => {
  76. return new Promise(resolve => {
  77. fixtureEl.innerHTML = '<a href="#">BS X</a>'
  78. const popoverEl = fixtureEl.querySelector('a')
  79. const popover = new Popover(popoverEl, {
  80. title: () => 'Bootstrap',
  81. content: () => 'loves writing tests (╯°□°)╯︵ ┻━┻'
  82. })
  83. popoverEl.addEventListener('shown.bs.popover', () => {
  84. const popoverDisplayed = document.querySelector('.popover')
  85. expect(popoverDisplayed).not.toBeNull()
  86. expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Bootstrap')
  87. expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('loves writing tests (╯°□°)╯︵ ┻━┻')
  88. resolve()
  89. })
  90. popover.show()
  91. })
  92. })
  93. it('should call content and title functions with trigger element', () => {
  94. return new Promise(resolve => {
  95. fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS X</a>'
  96. const popoverEl = fixtureEl.querySelector('a')
  97. const popover = new Popover(popoverEl, {
  98. title(el) {
  99. return el.dataset.foo
  100. },
  101. content(el) {
  102. return el.dataset.foo
  103. }
  104. })
  105. popoverEl.addEventListener('shown.bs.popover', () => {
  106. const popoverDisplayed = document.querySelector('.popover')
  107. expect(popoverDisplayed).not.toBeNull()
  108. expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
  109. expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
  110. resolve()
  111. })
  112. popover.show()
  113. })
  114. })
  115. it('should call content and title functions with correct this value', () => {
  116. return new Promise(resolve => {
  117. fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS X</a>'
  118. const popoverEl = fixtureEl.querySelector('a')
  119. const popover = new Popover(popoverEl, {
  120. title() {
  121. return this.dataset.foo
  122. },
  123. content() {
  124. return this.dataset.foo
  125. }
  126. })
  127. popoverEl.addEventListener('shown.bs.popover', () => {
  128. const popoverDisplayed = document.querySelector('.popover')
  129. expect(popoverDisplayed).not.toBeNull()
  130. expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
  131. expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
  132. resolve()
  133. })
  134. popover.show()
  135. })
  136. })
  137. it('should show a popover with just content without having header', () => {
  138. return new Promise(resolve => {
  139. fixtureEl.innerHTML = '<a href="#">Nice link</a>'
  140. const popoverEl = fixtureEl.querySelector('a')
  141. const popover = new Popover(popoverEl, {
  142. content: 'Some beautiful content :)'
  143. })
  144. popoverEl.addEventListener('shown.bs.popover', () => {
  145. const popoverDisplayed = document.querySelector('.popover')
  146. expect(popoverDisplayed).not.toBeNull()
  147. expect(popoverDisplayed.querySelector('.popover-header')).toBeNull()
  148. expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Some beautiful content :)')
  149. resolve()
  150. })
  151. popover.show()
  152. })
  153. })
  154. it('should show a popover with just title without having body', () => {
  155. return new Promise(resolve => {
  156. fixtureEl.innerHTML = '<a href="#">Nice link</a>'
  157. const popoverEl = fixtureEl.querySelector('a')
  158. const popover = new Popover(popoverEl, {
  159. title: 'Title which does not require content'
  160. })
  161. popoverEl.addEventListener('shown.bs.popover', () => {
  162. const popoverDisplayed = document.querySelector('.popover')
  163. expect(popoverDisplayed).not.toBeNull()
  164. expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
  165. expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
  166. resolve()
  167. })
  168. popover.show()
  169. })
  170. })
  171. it('should show a popover with just title without having body using data-attribute to get config', () => {
  172. return new Promise(resolve => {
  173. fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="Title which does not require content">Nice link</a>'
  174. const popoverEl = fixtureEl.querySelector('a')
  175. const popover = new Popover(popoverEl)
  176. popoverEl.addEventListener('shown.bs.popover', () => {
  177. const popoverDisplayed = document.querySelector('.popover')
  178. expect(popoverDisplayed).not.toBeNull()
  179. expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
  180. expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
  181. resolve()
  182. })
  183. popover.show()
  184. })
  185. })
  186. it('should NOT show a popover without `title` and `content`', () => {
  187. fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="">Nice link</a>'
  188. const popoverEl = fixtureEl.querySelector('a')
  189. const popover = new Popover(popoverEl, { animation: false })
  190. const spy = spyOn(EventHandler, 'trigger').and.callThrough()
  191. popover.show()
  192. expect(spy).not.toHaveBeenCalledWith(popoverEl, Popover.eventName('show'))
  193. expect(document.querySelector('.popover')).toBeNull()
  194. })
  195. it('"setContent" should keep the initial template', () => {
  196. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap" data-bs-custom-class="custom-class">BS X</a>'
  197. const popoverEl = fixtureEl.querySelector('a')
  198. const popover = new Popover(popoverEl)
  199. popover.setContent({ '.tooltip-inner': 'foo' })
  200. const tip = popover._getTipElement()
  201. expect(tip).toHaveClass('popover')
  202. expect(tip).toHaveClass('bs-popover-auto')
  203. expect(tip.querySelector('.popover-arrow')).not.toBeNull()
  204. expect(tip.querySelector('.popover-header')).not.toBeNull()
  205. expect(tip.querySelector('.popover-body')).not.toBeNull()
  206. })
  207. it('should call setContent once', () => {
  208. return new Promise(resolve => {
  209. fixtureEl.innerHTML = '<a href="#">BS X</a>'
  210. const popoverEl = fixtureEl.querySelector('a')
  211. const popover = new Popover(popoverEl, {
  212. content: 'Popover content'
  213. })
  214. expect(popover._templateFactory).toBeNull()
  215. let spy = null
  216. let times = 1
  217. popoverEl.addEventListener('hidden.bs.popover', () => {
  218. popover.show()
  219. })
  220. popoverEl.addEventListener('shown.bs.popover', () => {
  221. spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
  222. const popoverDisplayed = document.querySelector('.popover')
  223. expect(popoverDisplayed).not.toBeNull()
  224. expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
  225. expect(spy).toHaveBeenCalledTimes(0)
  226. if (times > 1) {
  227. resolve()
  228. }
  229. times++
  230. popover.hide()
  231. })
  232. popover.show()
  233. })
  234. })
  235. it('should show a popover with provided custom class', () => {
  236. return new Promise(resolve => {
  237. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap" data-bs-custom-class="custom-class">BS X</a>'
  238. const popoverEl = fixtureEl.querySelector('a')
  239. const popover = new Popover(popoverEl)
  240. popoverEl.addEventListener('shown.bs.popover', () => {
  241. const tip = document.querySelector('.popover')
  242. expect(tip).not.toBeNull()
  243. expect(tip).toHaveClass('custom-class')
  244. resolve()
  245. })
  246. popover.show()
  247. })
  248. })
  249. })
  250. describe('hide', () => {
  251. it('should hide a popover', () => {
  252. return new Promise(resolve => {
  253. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  254. const popoverEl = fixtureEl.querySelector('a')
  255. const popover = new Popover(popoverEl)
  256. popoverEl.addEventListener('shown.bs.popover', () => {
  257. popover.hide()
  258. })
  259. popoverEl.addEventListener('hidden.bs.popover', () => {
  260. expect(document.querySelector('.popover')).toBeNull()
  261. resolve()
  262. })
  263. popover.show()
  264. })
  265. })
  266. })
  267. describe('jQueryInterface', () => {
  268. it('should create a popover', () => {
  269. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  270. const popoverEl = fixtureEl.querySelector('a')
  271. jQueryMock.fn.popover = Popover.jQueryInterface
  272. jQueryMock.elements = [popoverEl]
  273. jQueryMock.fn.popover.call(jQueryMock)
  274. expect(Popover.getInstance(popoverEl)).not.toBeNull()
  275. })
  276. it('should create a popover with a config object', () => {
  277. fixtureEl.innerHTML = '<a href="#" title="Popover">BS X</a>'
  278. const popoverEl = fixtureEl.querySelector('a')
  279. jQueryMock.fn.popover = Popover.jQueryInterface
  280. jQueryMock.elements = [popoverEl]
  281. jQueryMock.fn.popover.call(jQueryMock, {
  282. content: 'Popover content'
  283. })
  284. expect(Popover.getInstance(popoverEl)).not.toBeNull()
  285. })
  286. it('should not re create a popover', () => {
  287. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  288. const popoverEl = fixtureEl.querySelector('a')
  289. const popover = new Popover(popoverEl)
  290. jQueryMock.fn.popover = Popover.jQueryInterface
  291. jQueryMock.elements = [popoverEl]
  292. jQueryMock.fn.popover.call(jQueryMock)
  293. expect(Popover.getInstance(popoverEl)).toEqual(popover)
  294. })
  295. it('should throw error on undefined method', () => {
  296. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  297. const popoverEl = fixtureEl.querySelector('a')
  298. const action = 'undefinedMethod'
  299. jQueryMock.fn.popover = Popover.jQueryInterface
  300. jQueryMock.elements = [popoverEl]
  301. expect(() => {
  302. jQueryMock.fn.popover.call(jQueryMock, action)
  303. }).toThrowError(TypeError, `No method named "${action}"`)
  304. })
  305. it('should should call show method', () => {
  306. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  307. const popoverEl = fixtureEl.querySelector('a')
  308. const popover = new Popover(popoverEl)
  309. jQueryMock.fn.popover = Popover.jQueryInterface
  310. jQueryMock.elements = [popoverEl]
  311. const spy = spyOn(popover, 'show')
  312. jQueryMock.fn.popover.call(jQueryMock, 'show')
  313. expect(spy).toHaveBeenCalled()
  314. })
  315. })
  316. describe('getInstance', () => {
  317. it('should return popover instance', () => {
  318. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  319. const popoverEl = fixtureEl.querySelector('a')
  320. const popover = new Popover(popoverEl)
  321. expect(Popover.getInstance(popoverEl)).toEqual(popover)
  322. expect(Popover.getInstance(popoverEl)).toBeInstanceOf(Popover)
  323. })
  324. it('should return null when there is no popover instance', () => {
  325. fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://x.com/getbootstrap">BS X</a>'
  326. const popoverEl = fixtureEl.querySelector('a')
  327. expect(Popover.getInstance(popoverEl)).toBeNull()
  328. })
  329. })
  330. describe('getOrCreateInstance', () => {
  331. it('should return popover instance', () => {
  332. fixtureEl.innerHTML = '<div></div>'
  333. const div = fixtureEl.querySelector('div')
  334. const popover = new Popover(div)
  335. expect(Popover.getOrCreateInstance(div)).toEqual(popover)
  336. expect(Popover.getInstance(div)).toEqual(Popover.getOrCreateInstance(div, {}))
  337. expect(Popover.getOrCreateInstance(div)).toBeInstanceOf(Popover)
  338. })
  339. it('should return new instance when there is no popover instance', () => {
  340. fixtureEl.innerHTML = '<div></div>'
  341. const div = fixtureEl.querySelector('div')
  342. expect(Popover.getInstance(div)).toBeNull()
  343. expect(Popover.getOrCreateInstance(div)).toBeInstanceOf(Popover)
  344. })
  345. it('should return new instance when there is no popover instance with given configuration', () => {
  346. fixtureEl.innerHTML = '<div></div>'
  347. const div = fixtureEl.querySelector('div')
  348. expect(Popover.getInstance(div)).toBeNull()
  349. const popover = Popover.getOrCreateInstance(div, {
  350. placement: 'top'
  351. })
  352. expect(popover).toBeInstanceOf(Popover)
  353. expect(popover._config.placement).toEqual('top')
  354. })
  355. it('should return the instance when exists without given configuration', () => {
  356. fixtureEl.innerHTML = '<div></div>'
  357. const div = fixtureEl.querySelector('div')
  358. const popover = new Popover(div, {
  359. placement: 'top'
  360. })
  361. expect(Popover.getInstance(div)).toEqual(popover)
  362. const popover2 = Popover.getOrCreateInstance(div, {
  363. placement: 'bottom'
  364. })
  365. expect(popover).toBeInstanceOf(Popover)
  366. expect(popover2).toEqual(popover)
  367. expect(popover2._config.placement).toEqual('top')
  368. })
  369. })
  370. })