index.spec.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. import * as Util from '../../../src/util/index'
  2. /** Test helpers */
  3. import { clearFixture, getFixture } from '../../helpers/fixture'
  4. describe('Util', () => {
  5. let fixtureEl
  6. beforeAll(() => {
  7. fixtureEl = getFixture()
  8. })
  9. afterEach(() => {
  10. clearFixture()
  11. })
  12. describe('getUID', () => {
  13. it('should generate uid', () => {
  14. const uid = Util.getUID('bs')
  15. const uid2 = Util.getUID('bs')
  16. expect(uid).not.toEqual(uid2)
  17. })
  18. })
  19. describe('getSelectorFromElement', () => {
  20. it('should get selector from data-bs-target', () => {
  21. fixtureEl.innerHTML = [
  22. '<div id="test" data-bs-target=".target"></div>',
  23. '<div class="target"></div>'
  24. ].join('')
  25. const testEl = fixtureEl.querySelector('#test')
  26. expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
  27. })
  28. it('should get selector from href if no data-bs-target set', () => {
  29. fixtureEl.innerHTML = [
  30. '<a id="test" href=".target"></a>',
  31. '<div class="target"></div>'
  32. ].join('')
  33. const testEl = fixtureEl.querySelector('#test')
  34. expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
  35. })
  36. it('should get selector from href if data-bs-target equal to #', () => {
  37. fixtureEl.innerHTML = [
  38. '<a id="test" data-bs-target="#" href=".target"></a>',
  39. '<div class="target"></div>'
  40. ].join('')
  41. const testEl = fixtureEl.querySelector('#test')
  42. expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
  43. })
  44. it('should return null if a selector from a href is a url without an anchor', () => {
  45. fixtureEl.innerHTML = [
  46. '<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
  47. '<div class="target"></div>'
  48. ].join('')
  49. const testEl = fixtureEl.querySelector('#test')
  50. expect(Util.getSelectorFromElement(testEl)).toBeNull()
  51. })
  52. it('should return the anchor if a selector from a href is a url', () => {
  53. fixtureEl.innerHTML = [
  54. '<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
  55. '<div id="target"></div>'
  56. ].join('')
  57. const testEl = fixtureEl.querySelector('#test')
  58. expect(Util.getSelectorFromElement(testEl)).toEqual('#target')
  59. })
  60. it('should return null if selector not found', () => {
  61. fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
  62. const testEl = fixtureEl.querySelector('#test')
  63. expect(Util.getSelectorFromElement(testEl)).toBeNull()
  64. })
  65. it('should return null if no selector', () => {
  66. fixtureEl.innerHTML = '<div></div>'
  67. const testEl = fixtureEl.querySelector('div')
  68. expect(Util.getSelectorFromElement(testEl)).toBeNull()
  69. })
  70. })
  71. describe('getElementFromSelector', () => {
  72. it('should get element from data-bs-target', () => {
  73. fixtureEl.innerHTML = [
  74. '<div id="test" data-bs-target=".target"></div>',
  75. '<div class="target"></div>'
  76. ].join('')
  77. const testEl = fixtureEl.querySelector('#test')
  78. expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
  79. })
  80. it('should get element from href if no data-bs-target set', () => {
  81. fixtureEl.innerHTML = [
  82. '<a id="test" href=".target"></a>',
  83. '<div class="target"></div>'
  84. ].join('')
  85. const testEl = fixtureEl.querySelector('#test')
  86. expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
  87. })
  88. it('should return null if element not found', () => {
  89. fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
  90. const testEl = fixtureEl.querySelector('#test')
  91. expect(Util.getElementFromSelector(testEl)).toBeNull()
  92. })
  93. it('should return null if no selector', () => {
  94. fixtureEl.innerHTML = '<div></div>'
  95. const testEl = fixtureEl.querySelector('div')
  96. expect(Util.getElementFromSelector(testEl)).toBeNull()
  97. })
  98. })
  99. describe('getTransitionDurationFromElement', () => {
  100. it('should get transition from element', () => {
  101. fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
  102. expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(300)
  103. })
  104. it('should return 0 if the element is undefined or null', () => {
  105. expect(Util.getTransitionDurationFromElement(null)).toEqual(0)
  106. expect(Util.getTransitionDurationFromElement(undefined)).toEqual(0)
  107. })
  108. it('should return 0 if the element do not possess transition', () => {
  109. fixtureEl.innerHTML = '<div></div>'
  110. expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(0)
  111. })
  112. })
  113. describe('triggerTransitionEnd', () => {
  114. it('should trigger transitionend event', done => {
  115. fixtureEl.innerHTML = '<div></div>'
  116. const el = fixtureEl.querySelector('div')
  117. const spy = spyOn(el, 'dispatchEvent').and.callThrough()
  118. el.addEventListener('transitionend', () => {
  119. expect(spy).toHaveBeenCalled()
  120. done()
  121. })
  122. Util.triggerTransitionEnd(el)
  123. })
  124. })
  125. describe('isElement', () => {
  126. it('should detect if the parameter is an element or not and return Boolean', () => {
  127. fixtureEl.innerHTML =
  128. [
  129. '<div id="foo" class="test"></div>',
  130. '<div id="bar" class="test"></div>'
  131. ].join('')
  132. const el = fixtureEl.querySelector('#foo')
  133. expect(Util.isElement(el)).toEqual(true)
  134. expect(Util.isElement({})).toEqual(false)
  135. expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toEqual(false)
  136. })
  137. it('should detect jQuery element', () => {
  138. fixtureEl.innerHTML = '<div></div>'
  139. const el = fixtureEl.querySelector('div')
  140. const fakejQuery = {
  141. 0: el,
  142. jquery: 'foo'
  143. }
  144. expect(Util.isElement(fakejQuery)).toEqual(true)
  145. })
  146. })
  147. describe('getElement', () => {
  148. it('should try to parse element', () => {
  149. fixtureEl.innerHTML =
  150. [
  151. '<div id="foo" class="test"></div>',
  152. '<div id="bar" class="test"></div>'
  153. ].join('')
  154. const el = fixtureEl.querySelector('div')
  155. expect(Util.getElement(el)).toEqual(el)
  156. expect(Util.getElement('#foo')).toEqual(el)
  157. expect(Util.getElement('#fail')).toBeNull()
  158. expect(Util.getElement({})).toBeNull()
  159. expect(Util.getElement([])).toBeNull()
  160. expect(Util.getElement()).toBeNull()
  161. expect(Util.getElement(null)).toBeNull()
  162. expect(Util.getElement(fixtureEl.querySelectorAll('.test'))).toBeNull()
  163. const fakejQueryObject = {
  164. 0: el,
  165. jquery: 'foo'
  166. }
  167. expect(Util.getElement(fakejQueryObject)).toEqual(el)
  168. })
  169. })
  170. describe('typeCheckConfig', () => {
  171. const namePlugin = 'collapse'
  172. it('should check type of the config object', () => {
  173. const defaultType = {
  174. toggle: 'boolean',
  175. parent: '(string|element)'
  176. }
  177. const config = {
  178. toggle: true,
  179. parent: 777
  180. }
  181. expect(() => {
  182. Util.typeCheckConfig(namePlugin, config, defaultType)
  183. }).toThrowError(TypeError, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')
  184. })
  185. it('should return null stringified when null is passed', () => {
  186. const defaultType = {
  187. toggle: 'boolean',
  188. parent: '(null|element)'
  189. }
  190. const config = {
  191. toggle: true,
  192. parent: null
  193. }
  194. Util.typeCheckConfig(namePlugin, config, defaultType)
  195. expect().nothing()
  196. })
  197. it('should return undefined stringified when undefined is passed', () => {
  198. const defaultType = {
  199. toggle: 'boolean',
  200. parent: '(undefined|element)'
  201. }
  202. const config = {
  203. toggle: true,
  204. parent: undefined
  205. }
  206. Util.typeCheckConfig(namePlugin, config, defaultType)
  207. expect().nothing()
  208. })
  209. })
  210. describe('isVisible', () => {
  211. it('should return false if the element is not defined', () => {
  212. expect(Util.isVisible(null)).toEqual(false)
  213. expect(Util.isVisible(undefined)).toEqual(false)
  214. })
  215. it('should return false if the element provided is not a dom element', () => {
  216. expect(Util.isVisible({})).toEqual(false)
  217. })
  218. it('should return false if the element is not visible with display none', () => {
  219. fixtureEl.innerHTML = '<div style="display: none;"></div>'
  220. const div = fixtureEl.querySelector('div')
  221. expect(Util.isVisible(div)).toEqual(false)
  222. })
  223. it('should return false if the element is not visible with visibility hidden', () => {
  224. fixtureEl.innerHTML = '<div style="visibility: hidden;"></div>'
  225. const div = fixtureEl.querySelector('div')
  226. expect(Util.isVisible(div)).toEqual(false)
  227. })
  228. it('should return false if an ancestor element is display none', () => {
  229. fixtureEl.innerHTML = [
  230. '<div style="display: none;">',
  231. ' <div>',
  232. ' <div>',
  233. ' <div class="content"></div>',
  234. ' </div>',
  235. ' </div>',
  236. '</div>'
  237. ].join('')
  238. const div = fixtureEl.querySelector('.content')
  239. expect(Util.isVisible(div)).toEqual(false)
  240. })
  241. it('should return false if an ancestor element is visibility hidden', () => {
  242. fixtureEl.innerHTML = [
  243. '<div style="visibility: hidden;">',
  244. ' <div>',
  245. ' <div>',
  246. ' <div class="content"></div>',
  247. ' </div>',
  248. ' </div>',
  249. '</div>'
  250. ].join('')
  251. const div = fixtureEl.querySelector('.content')
  252. expect(Util.isVisible(div)).toEqual(false)
  253. })
  254. it('should return true if an ancestor element is visibility hidden, but reverted', () => {
  255. fixtureEl.innerHTML = [
  256. '<div style="visibility: hidden;">',
  257. ' <div style="visibility: visible;">',
  258. ' <div>',
  259. ' <div class="content"></div>',
  260. ' </div>',
  261. ' </div>',
  262. '</div>'
  263. ].join('')
  264. const div = fixtureEl.querySelector('.content')
  265. expect(Util.isVisible(div)).toEqual(true)
  266. })
  267. it('should return true if the element is visible', () => {
  268. fixtureEl.innerHTML = [
  269. '<div>',
  270. ' <div id="element"></div>',
  271. '</div>'
  272. ].join('')
  273. const div = fixtureEl.querySelector('#element')
  274. expect(Util.isVisible(div)).toEqual(true)
  275. })
  276. it('should return false if the element is hidden, but not via display or visibility', () => {
  277. fixtureEl.innerHTML = [
  278. '<details>',
  279. ' <div id="element"></div>',
  280. '</details>'
  281. ].join('')
  282. const div = fixtureEl.querySelector('#element')
  283. expect(Util.isVisible(div)).toEqual(false)
  284. })
  285. })
  286. describe('isDisabled', () => {
  287. it('should return true if the element is not defined', () => {
  288. expect(Util.isDisabled(null)).toEqual(true)
  289. expect(Util.isDisabled(undefined)).toEqual(true)
  290. expect(Util.isDisabled()).toEqual(true)
  291. })
  292. it('should return true if the element provided is not a dom element', () => {
  293. expect(Util.isDisabled({})).toEqual(true)
  294. expect(Util.isDisabled('test')).toEqual(true)
  295. })
  296. it('should return true if the element has disabled attribute', () => {
  297. fixtureEl.innerHTML = [
  298. '<div>',
  299. ' <div id="element" disabled="disabled"></div>',
  300. ' <div id="element1" disabled="true"></div>',
  301. ' <div id="element2" disabled></div>',
  302. '</div>'
  303. ].join('')
  304. const div = fixtureEl.querySelector('#element')
  305. const div1 = fixtureEl.querySelector('#element1')
  306. const div2 = fixtureEl.querySelector('#element2')
  307. expect(Util.isDisabled(div)).toEqual(true)
  308. expect(Util.isDisabled(div1)).toEqual(true)
  309. expect(Util.isDisabled(div2)).toEqual(true)
  310. })
  311. it('should return false if the element has disabled attribute with "false" value, or doesn\'t have attribute', () => {
  312. fixtureEl.innerHTML = [
  313. '<div>',
  314. ' <div id="element" disabled="false"></div>',
  315. ' <div id="element1" ></div>',
  316. '</div>'
  317. ].join('')
  318. const div = fixtureEl.querySelector('#element')
  319. const div1 = fixtureEl.querySelector('#element1')
  320. expect(Util.isDisabled(div)).toEqual(false)
  321. expect(Util.isDisabled(div1)).toEqual(false)
  322. })
  323. it('should return false if the element is not disabled ', () => {
  324. fixtureEl.innerHTML = [
  325. '<div>',
  326. ' <button id="button"></button>',
  327. ' <select id="select"></select>',
  328. ' <select id="input"></select>',
  329. '</div>'
  330. ].join('')
  331. const el = selector => fixtureEl.querySelector(selector)
  332. expect(Util.isDisabled(el('#button'))).toEqual(false)
  333. expect(Util.isDisabled(el('#select'))).toEqual(false)
  334. expect(Util.isDisabled(el('#input'))).toEqual(false)
  335. })
  336. it('should return true if the element has disabled attribute', () => {
  337. fixtureEl.innerHTML = [
  338. '<div>',
  339. ' <input id="input" disabled="disabled"/>',
  340. ' <input id="input1" disabled="disabled"/>',
  341. ' <button id="button" disabled="true"></button>',
  342. ' <button id="button1" disabled="disabled"></button>',
  343. ' <button id="button2" disabled></button>',
  344. ' <select id="select" disabled></select>',
  345. ' <select id="input" disabled></select>',
  346. '</div>'
  347. ].join('')
  348. const el = selector => fixtureEl.querySelector(selector)
  349. expect(Util.isDisabled(el('#input'))).toEqual(true)
  350. expect(Util.isDisabled(el('#input1'))).toEqual(true)
  351. expect(Util.isDisabled(el('#button'))).toEqual(true)
  352. expect(Util.isDisabled(el('#button1'))).toEqual(true)
  353. expect(Util.isDisabled(el('#button2'))).toEqual(true)
  354. expect(Util.isDisabled(el('#input'))).toEqual(true)
  355. })
  356. it('should return true if the element has class "disabled"', () => {
  357. fixtureEl.innerHTML = [
  358. '<div>',
  359. ' <div id="element" class="disabled"></div>',
  360. '</div>'
  361. ].join('')
  362. const div = fixtureEl.querySelector('#element')
  363. expect(Util.isDisabled(div)).toEqual(true)
  364. })
  365. it('should return true if the element has class "disabled" but disabled attribute is false', () => {
  366. fixtureEl.innerHTML = [
  367. '<div>',
  368. ' <input id="input" class="disabled" disabled="false"/>',
  369. '</div>'
  370. ].join('')
  371. const div = fixtureEl.querySelector('#input')
  372. expect(Util.isDisabled(div)).toEqual(true)
  373. })
  374. })
  375. describe('findShadowRoot', () => {
  376. it('should return null if shadow dom is not available', () => {
  377. // Only for newer browsers
  378. if (!document.documentElement.attachShadow) {
  379. expect().nothing()
  380. return
  381. }
  382. fixtureEl.innerHTML = '<div></div>'
  383. const div = fixtureEl.querySelector('div')
  384. spyOn(document.documentElement, 'attachShadow').and.returnValue(null)
  385. expect(Util.findShadowRoot(div)).toEqual(null)
  386. })
  387. it('should return null when we do not find a shadow root', () => {
  388. // Only for newer browsers
  389. if (!document.documentElement.attachShadow) {
  390. expect().nothing()
  391. return
  392. }
  393. spyOn(document, 'getRootNode').and.returnValue(undefined)
  394. expect(Util.findShadowRoot(document)).toEqual(null)
  395. })
  396. it('should return the shadow root when found', () => {
  397. // Only for newer browsers
  398. if (!document.documentElement.attachShadow) {
  399. expect().nothing()
  400. return
  401. }
  402. fixtureEl.innerHTML = '<div></div>'
  403. const div = fixtureEl.querySelector('div')
  404. const shadowRoot = div.attachShadow({
  405. mode: 'open'
  406. })
  407. expect(Util.findShadowRoot(shadowRoot)).toEqual(shadowRoot)
  408. shadowRoot.innerHTML = '<button>Shadow Button</button>'
  409. expect(Util.findShadowRoot(shadowRoot.firstChild)).toEqual(shadowRoot)
  410. })
  411. })
  412. describe('noop', () => {
  413. it('should be a function', () => {
  414. expect(typeof Util.noop).toEqual('function')
  415. })
  416. })
  417. describe('reflow', () => {
  418. it('should return element offset height to force the reflow', () => {
  419. fixtureEl.innerHTML = '<div></div>'
  420. const div = fixtureEl.querySelector('div')
  421. expect(Util.reflow(div)).toEqual(0)
  422. })
  423. })
  424. describe('getjQuery', () => {
  425. const fakejQuery = { trigger() {} }
  426. beforeEach(() => {
  427. Object.defineProperty(window, 'jQuery', {
  428. value: fakejQuery,
  429. writable: true
  430. })
  431. })
  432. afterEach(() => {
  433. window.jQuery = undefined
  434. })
  435. it('should return jQuery object when present', () => {
  436. expect(Util.getjQuery()).toEqual(fakejQuery)
  437. })
  438. it('should not return jQuery object when present if data-bs-no-jquery', () => {
  439. document.body.setAttribute('data-bs-no-jquery', '')
  440. expect(window.jQuery).toEqual(fakejQuery)
  441. expect(Util.getjQuery()).toEqual(null)
  442. document.body.removeAttribute('data-bs-no-jquery')
  443. })
  444. it('should not return jQuery if not present', () => {
  445. window.jQuery = undefined
  446. expect(Util.getjQuery()).toEqual(null)
  447. })
  448. })
  449. describe('onDOMContentLoaded', () => {
  450. it('should execute callbacks when DOMContentLoaded is fired and should not add more than one listener', () => {
  451. const spy = jasmine.createSpy()
  452. const spy2 = jasmine.createSpy()
  453. spyOn(document, 'addEventListener').and.callThrough()
  454. spyOnProperty(document, 'readyState').and.returnValue('loading')
  455. Util.onDOMContentLoaded(spy)
  456. Util.onDOMContentLoaded(spy2)
  457. document.dispatchEvent(new Event('DOMContentLoaded', {
  458. bubbles: true,
  459. cancelable: true
  460. }))
  461. expect(spy).toHaveBeenCalled()
  462. expect(spy2).toHaveBeenCalled()
  463. expect(document.addEventListener).toHaveBeenCalledTimes(1)
  464. })
  465. it('should execute callback if readyState is not "loading"', () => {
  466. const spy = jasmine.createSpy()
  467. Util.onDOMContentLoaded(spy)
  468. expect(spy).toHaveBeenCalled()
  469. })
  470. })
  471. describe('defineJQueryPlugin', () => {
  472. const fakejQuery = { fn: {} }
  473. beforeEach(() => {
  474. Object.defineProperty(window, 'jQuery', {
  475. value: fakejQuery,
  476. writable: true
  477. })
  478. })
  479. afterEach(() => {
  480. window.jQuery = undefined
  481. })
  482. it('should define a plugin on the jQuery instance', () => {
  483. const pluginMock = function () {}
  484. pluginMock.NAME = 'test'
  485. pluginMock.jQueryInterface = function () {}
  486. Util.defineJQueryPlugin(pluginMock)
  487. expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
  488. expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
  489. expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')
  490. })
  491. })
  492. describe('execute', () => {
  493. it('should execute if arg is function', () => {
  494. const spy = jasmine.createSpy('spy')
  495. Util.execute(spy)
  496. expect(spy).toHaveBeenCalled()
  497. })
  498. })
  499. describe('executeAfterTransition', () => {
  500. it('should immediately execute a function when waitForTransition parameter is false', () => {
  501. const el = document.createElement('div')
  502. const callbackSpy = jasmine.createSpy('callback spy')
  503. const eventListenerSpy = spyOn(el, 'addEventListener')
  504. Util.executeAfterTransition(callbackSpy, el, false)
  505. expect(callbackSpy).toHaveBeenCalled()
  506. expect(eventListenerSpy).not.toHaveBeenCalled()
  507. })
  508. it('should execute a function when a transitionend event is dispatched', () => {
  509. const el = document.createElement('div')
  510. const callbackSpy = jasmine.createSpy('callback spy')
  511. spyOn(window, 'getComputedStyle').and.returnValue({
  512. transitionDuration: '0.05s',
  513. transitionDelay: '0s'
  514. })
  515. Util.executeAfterTransition(callbackSpy, el)
  516. el.dispatchEvent(new TransitionEvent('transitionend'))
  517. expect(callbackSpy).toHaveBeenCalled()
  518. })
  519. it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', done => {
  520. const el = document.createElement('div')
  521. const callbackSpy = jasmine.createSpy('callback spy')
  522. spyOn(window, 'getComputedStyle').and.returnValue({
  523. transitionDuration: '0.05s',
  524. transitionDelay: '0s'
  525. })
  526. Util.executeAfterTransition(callbackSpy, el)
  527. setTimeout(() => {
  528. expect(callbackSpy).toHaveBeenCalled()
  529. done()
  530. }, 70)
  531. })
  532. it('should not execute a function a second time after a computed CSS transition duration and if a transitionend event has already been dispatched', done => {
  533. const el = document.createElement('div')
  534. const callbackSpy = jasmine.createSpy('callback spy')
  535. spyOn(window, 'getComputedStyle').and.returnValue({
  536. transitionDuration: '0.05s',
  537. transitionDelay: '0s'
  538. })
  539. Util.executeAfterTransition(callbackSpy, el)
  540. setTimeout(() => {
  541. el.dispatchEvent(new TransitionEvent('transitionend'))
  542. }, 50)
  543. setTimeout(() => {
  544. expect(callbackSpy).toHaveBeenCalledTimes(1)
  545. done()
  546. }, 70)
  547. })
  548. it('should not trigger a transitionend event if another transitionend event had already happened', done => {
  549. const el = document.createElement('div')
  550. spyOn(window, 'getComputedStyle').and.returnValue({
  551. transitionDuration: '0.05s',
  552. transitionDelay: '0s'
  553. })
  554. Util.executeAfterTransition(() => {}, el)
  555. // simulate a event dispatched by the browser
  556. el.dispatchEvent(new TransitionEvent('transitionend'))
  557. const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
  558. setTimeout(() => {
  559. // setTimeout should not have triggered another transitionend event.
  560. expect(dispatchSpy).not.toHaveBeenCalled()
  561. done()
  562. }, 70)
  563. })
  564. it('should ignore transitionend events from nested elements', done => {
  565. fixtureEl.innerHTML = [
  566. '<div class="outer">',
  567. ' <div class="nested"></div>',
  568. '</div>'
  569. ].join('')
  570. const outer = fixtureEl.querySelector('.outer')
  571. const nested = fixtureEl.querySelector('.nested')
  572. const callbackSpy = jasmine.createSpy('callback spy')
  573. spyOn(window, 'getComputedStyle').and.returnValue({
  574. transitionDuration: '0.05s',
  575. transitionDelay: '0s'
  576. })
  577. Util.executeAfterTransition(callbackSpy, outer)
  578. nested.dispatchEvent(new TransitionEvent('transitionend', {
  579. bubbles: true
  580. }))
  581. setTimeout(() => {
  582. expect(callbackSpy).not.toHaveBeenCalled()
  583. }, 20)
  584. setTimeout(() => {
  585. expect(callbackSpy).toHaveBeenCalled()
  586. done()
  587. }, 70)
  588. })
  589. })
  590. describe('getNextActiveElement', () => {
  591. it('should return first element if active not exists or not given and shouldGetNext is either true, or false with cycling being disabled', () => {
  592. const array = ['a', 'b', 'c', 'd']
  593. expect(Util.getNextActiveElement(array, '', true, true)).toEqual('a')
  594. expect(Util.getNextActiveElement(array, 'g', true, true)).toEqual('a')
  595. expect(Util.getNextActiveElement(array, '', true, false)).toEqual('a')
  596. expect(Util.getNextActiveElement(array, 'g', true, false)).toEqual('a')
  597. expect(Util.getNextActiveElement(array, '', false, false)).toEqual('a')
  598. expect(Util.getNextActiveElement(array, 'g', false, false)).toEqual('a')
  599. })
  600. it('should return last element if active not exists or not given and shouldGetNext is false but cycling is enabled', () => {
  601. const array = ['a', 'b', 'c', 'd']
  602. expect(Util.getNextActiveElement(array, '', false, true)).toEqual('d')
  603. expect(Util.getNextActiveElement(array, 'g', false, true)).toEqual('d')
  604. })
  605. it('should return next element or same if is last', () => {
  606. const array = ['a', 'b', 'c', 'd']
  607. expect(Util.getNextActiveElement(array, 'a', true, true)).toEqual('b')
  608. expect(Util.getNextActiveElement(array, 'b', true, true)).toEqual('c')
  609. expect(Util.getNextActiveElement(array, 'd', true, false)).toEqual('d')
  610. })
  611. it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
  612. const array = ['a', 'b', 'c', 'd']
  613. expect(Util.getNextActiveElement(array, 'c', true, true)).toEqual('d')
  614. expect(Util.getNextActiveElement(array, 'd', true, true)).toEqual('a')
  615. })
  616. it('should return previous element or same if is first', () => {
  617. const array = ['a', 'b', 'c', 'd']
  618. expect(Util.getNextActiveElement(array, 'b', false, true)).toEqual('a')
  619. expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
  620. expect(Util.getNextActiveElement(array, 'a', false, false)).toEqual('a')
  621. })
  622. it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
  623. const array = ['a', 'b', 'c', 'd']
  624. expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
  625. expect(Util.getNextActiveElement(array, 'a', false, true)).toEqual('d')
  626. })
  627. })
  628. })