From 3ea39841c8049525e31e9f4d6300f0c60cdb42de Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 24 Jan 2023 13:33:51 +0100 Subject: Adding upstream version 5.2.3+dfsg. Signed-off-by: Daniel Baumann --- js/tests/unit/dom/data.spec.js | 106 +++++++ js/tests/unit/dom/event-handler.spec.js | 480 ++++++++++++++++++++++++++++++ js/tests/unit/dom/manipulator.spec.js | 135 +++++++++ js/tests/unit/dom/selector-engine.spec.js | 236 +++++++++++++++ 4 files changed, 957 insertions(+) create mode 100644 js/tests/unit/dom/data.spec.js create mode 100644 js/tests/unit/dom/event-handler.spec.js create mode 100644 js/tests/unit/dom/manipulator.spec.js create mode 100644 js/tests/unit/dom/selector-engine.spec.js (limited to 'js/tests/unit/dom') diff --git a/js/tests/unit/dom/data.spec.js b/js/tests/unit/dom/data.spec.js new file mode 100644 index 0000000..e898cbb --- /dev/null +++ b/js/tests/unit/dom/data.spec.js @@ -0,0 +1,106 @@ +import Data from '../../../src/dom/data' +import { getFixture, clearFixture } from '../../helpers/fixture' + +describe('Data', () => { + const TEST_KEY = 'bs.test' + const UNKNOWN_KEY = 'bs.unknown' + const TEST_DATA = { + test: 'bsData' + } + + let fixtureEl + let div + + beforeAll(() => { + fixtureEl = getFixture() + }) + + beforeEach(() => { + fixtureEl.innerHTML = '
' + div = fixtureEl.querySelector('div') + }) + + afterEach(() => { + Data.remove(div, TEST_KEY) + clearFixture() + }) + + it('should return null for unknown elements', () => { + const data = { ...TEST_DATA } + + Data.set(div, TEST_KEY, data) + + expect(Data.get(null)).toBeNull() + expect(Data.get(undefined)).toBeNull() + expect(Data.get(document.createElement('div'), TEST_KEY)).toBeNull() + }) + + it('should return null for unknown keys', () => { + const data = { ...TEST_DATA } + + Data.set(div, TEST_KEY, data) + + expect(Data.get(div, null)).toBeNull() + expect(Data.get(div, undefined)).toBeNull() + expect(Data.get(div, UNKNOWN_KEY)).toBeNull() + }) + + it('should store data for an element with a given key and return it', () => { + const data = { ...TEST_DATA } + + Data.set(div, TEST_KEY, data) + + expect(Data.get(div, TEST_KEY)).toEqual(data) + }) + + it('should overwrite data if something is already stored', () => { + const data = { ...TEST_DATA } + const copy = { ...data } + + Data.set(div, TEST_KEY, data) + Data.set(div, TEST_KEY, copy) + + // Using `toBe` since spread creates a shallow copy + expect(Data.get(div, TEST_KEY)).not.toBe(data) + expect(Data.get(div, TEST_KEY)).toBe(copy) + }) + + it('should do nothing when an element has nothing stored', () => { + Data.remove(div, TEST_KEY) + + expect().nothing() + }) + + it('should remove nothing for an unknown key', () => { + const data = { ...TEST_DATA } + + Data.set(div, TEST_KEY, data) + Data.remove(div, UNKNOWN_KEY) + + expect(Data.get(div, TEST_KEY)).toEqual(data) + }) + + it('should remove data for a given key', () => { + const data = { ...TEST_DATA } + + Data.set(div, TEST_KEY, data) + Data.remove(div, TEST_KEY) + + expect(Data.get(div, TEST_KEY)).toBeNull() + }) + + /* eslint-disable no-console */ + it('should console.error a message if called with multiple keys', () => { + console.error = jasmine.createSpy('console.error') + + const data = { ...TEST_DATA } + const copy = { ...data } + + Data.set(div, TEST_KEY, data) + Data.set(div, UNKNOWN_KEY, copy) + + expect(console.error).toHaveBeenCalled() + expect(Data.get(div, UNKNOWN_KEY)).toBeNull() + }) + /* eslint-enable no-console */ +}) diff --git a/js/tests/unit/dom/event-handler.spec.js b/js/tests/unit/dom/event-handler.spec.js new file mode 100644 index 0000000..623b9c1 --- /dev/null +++ b/js/tests/unit/dom/event-handler.spec.js @@ -0,0 +1,480 @@ +import EventHandler from '../../../src/dom/event-handler' +import { clearFixture, getFixture } from '../../helpers/fixture' +import { noop } from '../../../src/util' + +describe('EventHandler', () => { + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('on', () => { + it('should not add event listener if the event is not a string', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + EventHandler.on(div, null, noop) + EventHandler.on(null, 'click', noop) + + expect().nothing() + }) + + it('should add event listener', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + EventHandler.on(div, 'click', () => { + expect().nothing() + resolve() + }) + + div.click() + }) + }) + + it('should add namespaced event listener', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + EventHandler.on(div, 'bs.namespace', () => { + expect().nothing() + resolve() + }) + + EventHandler.trigger(div, 'bs.namespace') + }) + }) + + it('should add native namespaced event listener', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + EventHandler.on(div, 'click.namespace', () => { + expect().nothing() + resolve() + }) + + EventHandler.trigger(div, 'click') + }) + }) + + it('should handle event delegation', () => { + return new Promise(resolve => { + EventHandler.on(document, 'click', '.test', () => { + expect().nothing() + resolve() + }) + + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + div.click() + }) + }) + + it('should handle mouseenter/mouseleave like the native counterpart', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '
', + '
', + '
', + '
', + '
', + '
', + '
', + '
' + ].join('') + + const outer = fixtureEl.querySelector('.outer') + const inner = fixtureEl.querySelector('.inner') + const nested = fixtureEl.querySelector('.nested') + const deep = fixtureEl.querySelector('.deep') + const sibling = fixtureEl.querySelector('.sibling') + + const enterSpy = jasmine.createSpy('mouseenter') + const leaveSpy = jasmine.createSpy('mouseleave') + const delegateEnterSpy = jasmine.createSpy('mouseenter') + const delegateLeaveSpy = jasmine.createSpy('mouseleave') + + EventHandler.on(inner, 'mouseenter', enterSpy) + EventHandler.on(inner, 'mouseleave', leaveSpy) + EventHandler.on(outer, 'mouseenter', '.inner', delegateEnterSpy) + EventHandler.on(outer, 'mouseleave', '.inner', delegateLeaveSpy) + + EventHandler.on(sibling, 'mouseenter', () => { + expect(enterSpy.calls.count()).toEqual(2) + expect(leaveSpy.calls.count()).toEqual(2) + expect(delegateEnterSpy.calls.count()).toEqual(2) + expect(delegateLeaveSpy.calls.count()).toEqual(2) + resolve() + }) + + const moveMouse = (from, to) => { + from.dispatchEvent(new MouseEvent('mouseout', { + bubbles: true, + relatedTarget: to + })) + + to.dispatchEvent(new MouseEvent('mouseover', { + bubbles: true, + relatedTarget: from + })) + } + + // from outer to deep and back to outer (nested) + moveMouse(outer, inner) + moveMouse(inner, nested) + moveMouse(nested, deep) + moveMouse(deep, nested) + moveMouse(nested, inner) + moveMouse(inner, outer) + + setTimeout(() => { + expect(enterSpy.calls.count()).toEqual(1) + expect(leaveSpy.calls.count()).toEqual(1) + expect(delegateEnterSpy.calls.count()).toEqual(1) + expect(delegateLeaveSpy.calls.count()).toEqual(1) + + // from outer to inner to sibling (adjacent) + moveMouse(outer, inner) + moveMouse(inner, sibling) + }, 20) + }) + }) + }) + + describe('one', () => { + it('should call listener just once', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + let called = 0 + const div = fixtureEl.querySelector('div') + const obj = { + oneListener() { + called++ + } + } + + EventHandler.one(div, 'bootstrap', obj.oneListener) + + EventHandler.trigger(div, 'bootstrap') + EventHandler.trigger(div, 'bootstrap') + + setTimeout(() => { + expect(called).toEqual(1) + resolve() + }, 20) + }) + }) + + it('should call delegated listener just once', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + let called = 0 + const div = fixtureEl.querySelector('div') + const obj = { + oneListener() { + called++ + } + } + + EventHandler.one(fixtureEl, 'bootstrap', 'div', obj.oneListener) + + EventHandler.trigger(div, 'bootstrap') + EventHandler.trigger(div, 'bootstrap') + + setTimeout(() => { + expect(called).toEqual(1) + resolve() + }, 20) + }) + }) + }) + + describe('off', () => { + it('should not remove a listener', () => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + EventHandler.off(div, null, noop) + EventHandler.off(null, 'click', noop) + expect().nothing() + }) + + it('should remove a listener', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let called = 0 + const handler = () => { + called++ + } + + EventHandler.on(div, 'foobar', handler) + EventHandler.trigger(div, 'foobar') + + EventHandler.off(div, 'foobar', handler) + EventHandler.trigger(div, 'foobar') + + setTimeout(() => { + expect(called).toEqual(1) + resolve() + }, 20) + }) + }) + + it('should remove all the events', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let called = 0 + + EventHandler.on(div, 'foobar', () => { + called++ + }) + EventHandler.on(div, 'foobar', () => { + called++ + }) + EventHandler.trigger(div, 'foobar') + + EventHandler.off(div, 'foobar') + EventHandler.trigger(div, 'foobar') + + setTimeout(() => { + expect(called).toEqual(2) + resolve() + }, 20) + }) + }) + + it('should remove all the namespaced listeners if namespace is passed', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let called = 0 + + EventHandler.on(div, 'foobar.namespace', () => { + called++ + }) + EventHandler.on(div, 'foofoo.namespace', () => { + called++ + }) + EventHandler.trigger(div, 'foobar.namespace') + EventHandler.trigger(div, 'foofoo.namespace') + + EventHandler.off(div, '.namespace') + EventHandler.trigger(div, 'foobar.namespace') + EventHandler.trigger(div, 'foofoo.namespace') + + setTimeout(() => { + expect(called).toEqual(2) + resolve() + }, 20) + }) + }) + + it('should remove the namespaced listeners', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let calledCallback1 = 0 + let calledCallback2 = 0 + + EventHandler.on(div, 'foobar.namespace', () => { + calledCallback1++ + }) + EventHandler.on(div, 'foofoo.namespace', () => { + calledCallback2++ + }) + + EventHandler.trigger(div, 'foobar.namespace') + EventHandler.off(div, 'foobar.namespace') + EventHandler.trigger(div, 'foobar.namespace') + + EventHandler.trigger(div, 'foofoo.namespace') + + setTimeout(() => { + expect(calledCallback1).toEqual(1) + expect(calledCallback2).toEqual(1) + resolve() + }, 20) + }) + }) + + it('should remove the all the namespaced listeners for native events', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let called = 0 + + EventHandler.on(div, 'click.namespace', () => { + called++ + }) + EventHandler.on(div, 'click.namespace2', () => { + called++ + }) + + EventHandler.trigger(div, 'click') + EventHandler.off(div, 'click') + EventHandler.trigger(div, 'click') + + setTimeout(() => { + expect(called).toEqual(2) + resolve() + }, 20) + }) + }) + + it('should remove the specified namespaced listeners for native events', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + const div = fixtureEl.querySelector('div') + + let called1 = 0 + let called2 = 0 + + EventHandler.on(div, 'click.namespace', () => { + called1++ + }) + EventHandler.on(div, 'click.namespace2', () => { + called2++ + }) + EventHandler.trigger(div, 'click') + + EventHandler.off(div, 'click.namespace') + EventHandler.trigger(div, 'click') + + setTimeout(() => { + expect(called1).toEqual(1) + expect(called2).toEqual(2) + resolve() + }, 20) + }) + }) + + it('should remove a listener registered by .one', () => { + return new Promise((resolve, reject) => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const handler = () => { + reject(new Error('called')) + } + + EventHandler.one(div, 'foobar', handler) + EventHandler.off(div, 'foobar', handler) + + EventHandler.trigger(div, 'foobar') + setTimeout(() => { + expect().nothing() + resolve() + }, 20) + }) + }) + + it('should remove the correct delegated event listener', () => { + const element = document.createElement('div') + const subelement = document.createElement('span') + element.append(subelement) + + const anchor = document.createElement('a') + element.append(anchor) + + let i = 0 + const handler = () => { + i++ + } + + EventHandler.on(element, 'click', 'a', handler) + EventHandler.on(element, 'click', 'span', handler) + + fixtureEl.append(element) + + EventHandler.trigger(anchor, 'click') + EventHandler.trigger(subelement, 'click') + + // first listeners called + expect(i).toEqual(2) + + EventHandler.off(element, 'click', 'span', handler) + EventHandler.trigger(subelement, 'click') + + // removed listener not called + expect(i).toEqual(2) + + EventHandler.trigger(anchor, 'click') + + // not removed listener called + expect(i).toEqual(3) + + EventHandler.on(element, 'click', 'span', handler) + EventHandler.trigger(anchor, 'click') + EventHandler.trigger(subelement, 'click') + + // listener re-registered + expect(i).toEqual(5) + + EventHandler.off(element, 'click', 'span') + EventHandler.trigger(subelement, 'click') + + // listener removed again + expect(i).toEqual(5) + }) + }) + + describe('general functionality', () => { + it('should hydrate properties, and make them configurable', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '
', + '
', + '
', + '
' + ].join('') + + const div1 = fixtureEl.querySelector('#div1') + const div2 = fixtureEl.querySelector('#div2') + + EventHandler.on(div1, 'click', event => { + expect(event.currentTarget).toBe(div2) + expect(event.delegateTarget).toBe(div1) + expect(event.originalTarget).toBeNull() + + Object.defineProperty(event, 'currentTarget', { + configurable: true, + get() { + return div1 + } + }) + + expect(event.currentTarget).toBe(div1) + resolve() + }) + + expect(() => { + EventHandler.trigger(div1, 'click', { originalTarget: null, currentTarget: div2 }) + }).not.toThrowError(TypeError) + }) + }) + }) +}) diff --git a/js/tests/unit/dom/manipulator.spec.js b/js/tests/unit/dom/manipulator.spec.js new file mode 100644 index 0000000..4561e2e --- /dev/null +++ b/js/tests/unit/dom/manipulator.spec.js @@ -0,0 +1,135 @@ +import Manipulator from '../../../src/dom/manipulator' +import { clearFixture, getFixture } from '../../helpers/fixture' + +describe('Manipulator', () => { + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('setDataAttribute', () => { + it('should set data attribute prefixed with bs', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + Manipulator.setDataAttribute(div, 'key', 'value') + expect(div.getAttribute('data-bs-key')).toEqual('value') + }) + + it('should set data attribute in kebab case', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + Manipulator.setDataAttribute(div, 'testKey', 'value') + expect(div.getAttribute('data-bs-test-key')).toEqual('value') + }) + }) + + describe('removeDataAttribute', () => { + it('should only remove bs-prefixed data attribute', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + Manipulator.removeDataAttribute(div, 'key') + expect(div.getAttribute('data-bs-key')).toBeNull() + expect(div.getAttribute('data-key-bs')).toEqual('postfixed') + expect(div.getAttribute('data-key')).toEqual('value') + }) + + it('should remove data attribute in kebab case', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + Manipulator.removeDataAttribute(div, 'testKey') + expect(div.getAttribute('data-bs-test-key')).toBeNull() + }) + }) + + describe('getDataAttributes', () => { + it('should return an empty object for null', () => { + expect(Manipulator.getDataAttributes(null)).toEqual({}) + expect().nothing() + }) + + it('should get only bs-prefixed data attributes without bs namespace', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttributes(div)).toEqual({ + toggle: 'tabs', + target: '#element' + }) + }) + + it('should omit `bs-config` data attribute', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttributes(div)).toEqual({ + toggle: 'tabs', + target: '#element' + }) + }) + }) + + describe('getDataAttribute', () => { + it('should only get bs-prefixed data attribute', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttribute(div, 'key')).toEqual('value') + expect(Manipulator.getDataAttribute(div, 'test')).toBeNull() + expect(Manipulator.getDataAttribute(div, 'toggle')).toBeNull() + }) + + it('should get data attribute in kebab case', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttribute(div, 'testKey')).toEqual('value') + }) + + it('should normalize data', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttribute(div, 'test')).toBeFalse() + + div.setAttribute('data-bs-test', 'true') + expect(Manipulator.getDataAttribute(div, 'test')).toBeTrue() + + div.setAttribute('data-bs-test', '1') + expect(Manipulator.getDataAttribute(div, 'test')).toEqual(1) + }) + + it('should normalize json data', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Manipulator.getDataAttribute(div, 'test')).toEqual({ delay: { show: 100, hide: 10 } }) + + const objectData = { 'Super Hero': ['Iron Man', 'Super Man'], testNum: 90, url: 'http://localhost:8080/test?foo=bar' } + const dataStr = JSON.stringify(objectData) + div.setAttribute('data-bs-test', encodeURIComponent(dataStr)) + expect(Manipulator.getDataAttribute(div, 'test')).toEqual(objectData) + + div.setAttribute('data-bs-test', dataStr) + expect(Manipulator.getDataAttribute(div, 'test')).toEqual(objectData) + }) + }) +}) diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js new file mode 100644 index 0000000..0245896 --- /dev/null +++ b/js/tests/unit/dom/selector-engine.spec.js @@ -0,0 +1,236 @@ +import SelectorEngine from '../../../src/dom/selector-engine' +import { getFixture, clearFixture } from '../../helpers/fixture' + +describe('SelectorEngine', () => { + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('find', () => { + it('should find elements', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(SelectorEngine.find('div', fixtureEl)).toEqual([div]) + }) + + it('should find elements globally', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('#test') + + expect(SelectorEngine.find('#test')).toEqual([div]) + }) + + it('should handle :scope selectors', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const listEl = fixtureEl.querySelector('ul') + const aActive = fixtureEl.querySelector('.active') + + expect(SelectorEngine.find(':scope > li > .active', listEl)).toEqual([aActive]) + }) + }) + + describe('findOne', () => { + it('should return one element', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('#test') + + expect(SelectorEngine.findOne('#test')).toEqual(div) + }) + }) + + describe('children', () => { + it('should find children', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const list = fixtureEl.querySelector('ul') + const liList = [].concat(...fixtureEl.querySelectorAll('li')) + const result = SelectorEngine.children(list, 'li') + + expect(result).toEqual(liList) + }) + }) + + describe('parents', () => { + it('should return parents', () => { + expect(SelectorEngine.parents(fixtureEl, 'body')).toHaveSize(1) + }) + }) + + describe('prev', () => { + it('should return previous element', () => { + fixtureEl.innerHTML = '
' + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelector('.test') + + expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest]) + }) + + it('should return previous element with an extra element between', () => { + fixtureEl.innerHTML = [ + '
', + '', + '' + ].join('') + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelector('.test') + + expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest]) + }) + + it('should return previous element with comments or text nodes between', () => { + fixtureEl.innerHTML = [ + '
', + '
', + '', + 'Text', + '' + ].join('') + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelectorAll('.test')[1] + + expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest]) + }) + }) + + describe('next', () => { + it('should return next element', () => { + fixtureEl.innerHTML = '
' + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelector('.test') + + expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn]) + }) + + it('should return next element with an extra element between', () => { + fixtureEl.innerHTML = [ + '
', + '', + '' + ].join('') + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelector('.test') + + expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn]) + }) + + it('should return next element with comments or text nodes between', () => { + fixtureEl.innerHTML = [ + '
', + '', + 'Text', + '', + '' + ].join('') + + const btn = fixtureEl.querySelector('.btn') + const divTest = fixtureEl.querySelector('.test') + + expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn]) + }) + }) + + describe('focusableChildren', () => { + it('should return only elements with specific tag names', () => { + fixtureEl.innerHTML = [ + '
lorem
', + 'lorem', + 'lorem', + '', + '', + '', + '', + '
lorem
' + ].join('') + + const expectedElements = [ + fixtureEl.querySelector('a'), + fixtureEl.querySelector('button'), + fixtureEl.querySelector('input'), + fixtureEl.querySelector('textarea'), + fixtureEl.querySelector('select'), + fixtureEl.querySelector('details') + ] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + + it('should return any element with non negative tab index', () => { + fixtureEl.innerHTML = [ + '
lorem
', + '
lorem
', + '
lorem
' + ].join('') + + const expectedElements = [ + fixtureEl.querySelector('[tabindex]'), + fixtureEl.querySelector('[tabindex="0"]'), + fixtureEl.querySelector('[tabindex="10"]') + ] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + + it('should return not return elements with negative tab index', () => { + fixtureEl.innerHTML = '' + + const expectedElements = [] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + + it('should return contenteditable elements', () => { + fixtureEl.innerHTML = '
lorem
' + + const expectedElements = [fixtureEl.querySelector('[contenteditable="true"]')] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + + it('should not return disabled elements', () => { + fixtureEl.innerHTML = '' + + const expectedElements = [] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + + it('should not return invisible elements', () => { + fixtureEl.innerHTML = '' + + const expectedElements = [] + + expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements) + }) + }) +}) + -- cgit v1.2.3