selector-engine.spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. import SelectorEngine from '../../../src/dom/selector-engine.js'
  2. import { clearFixture, getFixture } from '../../helpers/fixture.js'
  3. describe('SelectorEngine', () => {
  4. let fixtureEl
  5. beforeAll(() => {
  6. fixtureEl = getFixture()
  7. })
  8. afterEach(() => {
  9. clearFixture()
  10. })
  11. describe('find', () => {
  12. it('should find elements', () => {
  13. fixtureEl.innerHTML = '<div></div>'
  14. const div = fixtureEl.querySelector('div')
  15. expect(SelectorEngine.find('div', fixtureEl)).toEqual([div])
  16. })
  17. it('should find elements globally', () => {
  18. fixtureEl.innerHTML = '<div id="test"></div>'
  19. const div = fixtureEl.querySelector('#test')
  20. expect(SelectorEngine.find('#test')).toEqual([div])
  21. })
  22. it('should handle :scope selectors', () => {
  23. fixtureEl.innerHTML = [
  24. '<ul>',
  25. ' <li></li>',
  26. ' <li>',
  27. ' <a href="#" class="active">link</a>',
  28. ' </li>',
  29. ' <li></li>',
  30. '</ul>'
  31. ].join('')
  32. const listEl = fixtureEl.querySelector('ul')
  33. const aActive = fixtureEl.querySelector('.active')
  34. expect(SelectorEngine.find(':scope > li > .active', listEl)).toEqual([aActive])
  35. })
  36. })
  37. describe('findOne', () => {
  38. it('should return one element', () => {
  39. fixtureEl.innerHTML = '<div id="test"></div>'
  40. const div = fixtureEl.querySelector('#test')
  41. expect(SelectorEngine.findOne('#test')).toEqual(div)
  42. })
  43. })
  44. describe('children', () => {
  45. it('should find children', () => {
  46. fixtureEl.innerHTML = [
  47. '<ul>',
  48. ' <li></li>',
  49. ' <li></li>',
  50. ' <li></li>',
  51. '</ul>'
  52. ].join('')
  53. const list = fixtureEl.querySelector('ul')
  54. const liList = [].concat(...fixtureEl.querySelectorAll('li'))
  55. const result = SelectorEngine.children(list, 'li')
  56. expect(result).toEqual(liList)
  57. })
  58. })
  59. describe('parents', () => {
  60. it('should return parents', () => {
  61. expect(SelectorEngine.parents(fixtureEl, 'body')).toHaveSize(1)
  62. })
  63. })
  64. describe('prev', () => {
  65. it('should return previous element', () => {
  66. fixtureEl.innerHTML = '<div class="test"></div><button class="btn"></button>'
  67. const btn = fixtureEl.querySelector('.btn')
  68. const divTest = fixtureEl.querySelector('.test')
  69. expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest])
  70. })
  71. it('should return previous element with an extra element between', () => {
  72. fixtureEl.innerHTML = [
  73. '<div class="test"></div>',
  74. '<span></span>',
  75. '<button class="btn"></button>'
  76. ].join('')
  77. const btn = fixtureEl.querySelector('.btn')
  78. const divTest = fixtureEl.querySelector('.test')
  79. expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest])
  80. })
  81. it('should return previous element with comments or text nodes between', () => {
  82. fixtureEl.innerHTML = [
  83. '<div class="test"></div>',
  84. '<div class="test"></div>',
  85. '<!-- Comment-->',
  86. 'Text',
  87. '<button class="btn"></button>'
  88. ].join('')
  89. const btn = fixtureEl.querySelector('.btn')
  90. const divTest = fixtureEl.querySelectorAll('.test')[1]
  91. expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest])
  92. })
  93. })
  94. describe('next', () => {
  95. it('should return next element', () => {
  96. fixtureEl.innerHTML = '<div class="test"></div><button class="btn"></button>'
  97. const btn = fixtureEl.querySelector('.btn')
  98. const divTest = fixtureEl.querySelector('.test')
  99. expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
  100. })
  101. it('should return next element with an extra element between', () => {
  102. fixtureEl.innerHTML = [
  103. '<div class="test"></div>',
  104. '<span></span>',
  105. '<button class="btn"></button>'
  106. ].join('')
  107. const btn = fixtureEl.querySelector('.btn')
  108. const divTest = fixtureEl.querySelector('.test')
  109. expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
  110. })
  111. it('should return next element with comments or text nodes between', () => {
  112. fixtureEl.innerHTML = [
  113. '<div class="test"></div>',
  114. '<!-- Comment-->',
  115. 'Text',
  116. '<button class="btn"></button>',
  117. '<button class="btn"></button>'
  118. ].join('')
  119. const btn = fixtureEl.querySelector('.btn')
  120. const divTest = fixtureEl.querySelector('.test')
  121. expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
  122. })
  123. })
  124. describe('focusableChildren', () => {
  125. it('should return only elements with specific tag names', () => {
  126. fixtureEl.innerHTML = [
  127. '<div>lorem</div>',
  128. '<span>lorem</span>',
  129. '<a>lorem</a>',
  130. '<button>lorem</button>',
  131. '<input>',
  132. '<textarea></textarea>',
  133. '<select></select>',
  134. '<details>lorem</details>'
  135. ].join('')
  136. const expectedElements = [
  137. fixtureEl.querySelector('a'),
  138. fixtureEl.querySelector('button'),
  139. fixtureEl.querySelector('input'),
  140. fixtureEl.querySelector('textarea'),
  141. fixtureEl.querySelector('select'),
  142. fixtureEl.querySelector('details')
  143. ]
  144. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  145. })
  146. it('should return any element with non negative tab index', () => {
  147. fixtureEl.innerHTML = [
  148. '<div tabindex>lorem</div>',
  149. '<div tabindex="0">lorem</div>',
  150. '<div tabindex="10">lorem</div>'
  151. ].join('')
  152. const expectedElements = [
  153. fixtureEl.querySelector('[tabindex]'),
  154. fixtureEl.querySelector('[tabindex="0"]'),
  155. fixtureEl.querySelector('[tabindex="10"]')
  156. ]
  157. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  158. })
  159. it('should return not return elements with negative tab index', () => {
  160. fixtureEl.innerHTML = '<button tabindex="-1">lorem</button>'
  161. const expectedElements = []
  162. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  163. })
  164. it('should return contenteditable elements', () => {
  165. fixtureEl.innerHTML = '<div contenteditable="true">lorem</div>'
  166. const expectedElements = [fixtureEl.querySelector('[contenteditable="true"]')]
  167. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  168. })
  169. it('should not return disabled elements', () => {
  170. fixtureEl.innerHTML = '<button disabled="true">lorem</button>'
  171. const expectedElements = []
  172. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  173. })
  174. it('should not return invisible elements', () => {
  175. fixtureEl.innerHTML = '<button style="display:none;">lorem</button>'
  176. const expectedElements = []
  177. expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
  178. })
  179. })
  180. describe('getSelectorFromElement', () => {
  181. it('should get selector from data-bs-target', () => {
  182. fixtureEl.innerHTML = [
  183. '<div id="test" data-bs-target=".target"></div>',
  184. '<div class="target"></div>'
  185. ].join('')
  186. const testEl = fixtureEl.querySelector('#test')
  187. expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
  188. })
  189. it('should get selector from href if no data-bs-target set', () => {
  190. fixtureEl.innerHTML = [
  191. '<a id="test" href=".target"></a>',
  192. '<div class="target"></div>'
  193. ].join('')
  194. const testEl = fixtureEl.querySelector('#test')
  195. expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
  196. })
  197. it('should get selector from href if data-bs-target equal to #', () => {
  198. fixtureEl.innerHTML = [
  199. '<a id="test" data-bs-target="#" href=".target"></a>',
  200. '<div class="target"></div>'
  201. ].join('')
  202. const testEl = fixtureEl.querySelector('#test')
  203. expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
  204. })
  205. it('should return null if a selector from a href is a url without an anchor', () => {
  206. fixtureEl.innerHTML = [
  207. '<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
  208. '<div class="target"></div>'
  209. ].join('')
  210. const testEl = fixtureEl.querySelector('#test')
  211. expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
  212. })
  213. it('should return the anchor if a selector from a href is a url', () => {
  214. fixtureEl.innerHTML = [
  215. '<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
  216. '<div id="target"></div>'
  217. ].join('')
  218. const testEl = fixtureEl.querySelector('#test')
  219. expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('#target')
  220. })
  221. it('should return null if selector not found', () => {
  222. fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
  223. const testEl = fixtureEl.querySelector('#test')
  224. expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
  225. })
  226. it('should return null if no selector', () => {
  227. fixtureEl.innerHTML = '<div></div>'
  228. const testEl = fixtureEl.querySelector('div')
  229. expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
  230. })
  231. })
  232. describe('getElementFromSelector', () => {
  233. it('should get element from data-bs-target', () => {
  234. fixtureEl.innerHTML = [
  235. '<div id="test" data-bs-target=".target"></div>',
  236. '<div class="target"></div>'
  237. ].join('')
  238. const testEl = fixtureEl.querySelector('#test')
  239. expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
  240. })
  241. it('should get element from href if no data-bs-target set', () => {
  242. fixtureEl.innerHTML = [
  243. '<a id="test" href=".target"></a>',
  244. '<div class="target"></div>'
  245. ].join('')
  246. const testEl = fixtureEl.querySelector('#test')
  247. expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
  248. })
  249. it('should return null if element not found', () => {
  250. fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
  251. const testEl = fixtureEl.querySelector('#test')
  252. expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
  253. })
  254. it('should return null if no selector', () => {
  255. fixtureEl.innerHTML = '<div></div>'
  256. const testEl = fixtureEl.querySelector('div')
  257. expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
  258. })
  259. })
  260. describe('getMultipleElementsFromSelector', () => {
  261. it('should get elements from data-bs-target', () => {
  262. fixtureEl.innerHTML = [
  263. '<div id="test" data-bs-target=".target"></div>',
  264. '<div class="target"></div>',
  265. '<div class="target"></div>'
  266. ].join('')
  267. const testEl = fixtureEl.querySelector('#test')
  268. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
  269. })
  270. it('should get elements if several ids are given', () => {
  271. fixtureEl.innerHTML = [
  272. '<div id="test" data-bs-target="#target1,#target2"></div>',
  273. '<div class="target" id="target1"></div>',
  274. '<div class="target" id="target2"></div>'
  275. ].join('')
  276. const testEl = fixtureEl.querySelector('#test')
  277. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
  278. })
  279. it('should get elements if several ids with special chars are given', () => {
  280. fixtureEl.innerHTML = [
  281. '<div id="test" data-bs-target="#j_id11:exampleModal,#j_id22:exampleModal"></div>',
  282. '<div class="target" id="j_id11:exampleModal"></div>',
  283. '<div class="target" id="j_id22:exampleModal"></div>'
  284. ].join('')
  285. const testEl = fixtureEl.querySelector('#test')
  286. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
  287. })
  288. it('should get elements in array, from href if no data-bs-target set', () => {
  289. fixtureEl.innerHTML = [
  290. '<a id="test" href=".target"></a>',
  291. '<div class="target"></div>',
  292. '<div class="target"></div>'
  293. ].join('')
  294. const testEl = fixtureEl.querySelector('#test')
  295. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
  296. })
  297. it('should return empty array if elements not found', () => {
  298. fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
  299. const testEl = fixtureEl.querySelector('#test')
  300. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
  301. })
  302. it('should return empty array if no selector', () => {
  303. fixtureEl.innerHTML = '<div></div>'
  304. const testEl = fixtureEl.querySelector('div')
  305. expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
  306. })
  307. })
  308. })