diff options
Diffstat (limited to 'js/tests/unit/util')
| -rw-r--r-- | js/tests/unit/util/backdrop.spec.js | 4 | ||||
| -rw-r--r-- | js/tests/unit/util/component-functions.spec.js | 108 | ||||
| -rw-r--r-- | js/tests/unit/util/focustrap.spec.js | 210 | ||||
| -rw-r--r-- | js/tests/unit/util/index.spec.js | 5 |
4 files changed, 323 insertions, 4 deletions
diff --git a/js/tests/unit/util/backdrop.spec.js b/js/tests/unit/util/backdrop.spec.js index 59b789071..b885b60b5 100644 --- a/js/tests/unit/util/backdrop.spec.js +++ b/js/tests/unit/util/backdrop.spec.js @@ -18,7 +18,7 @@ describe('Backdrop', () => { const list = document.querySelectorAll(CLASS_BACKDROP) list.forEach(el => { - document.body.removeChild(el) + el.remove() }) }) @@ -141,7 +141,7 @@ describe('Backdrop', () => { const getElements = () => document.querySelectorAll(CLASS_BACKDROP) instance.show(() => { - wrapper.parentNode.removeChild(wrapper) + wrapper.remove() instance.hide(() => { expect(getElements().length).toEqual(0) done() diff --git a/js/tests/unit/util/component-functions.spec.js b/js/tests/unit/util/component-functions.spec.js new file mode 100644 index 000000000..edaedd32e --- /dev/null +++ b/js/tests/unit/util/component-functions.spec.js @@ -0,0 +1,108 @@ +/* Test helpers */ + +import { clearFixture, createEvent, getFixture } from '../../helpers/fixture' +import { enableDismissTrigger } from '../../../src/util/component-functions' +import BaseComponent from '../../../src/base-component' + +class DummyClass2 extends BaseComponent { + static get NAME() { + return 'test' + } + + hide() { + return true + } + + testMethod() { + return true + } +} + +describe('Plugin functions', () => { + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('data-bs-dismiss functionality', () => { + it('should get Plugin and execute the given method, when a click occurred on data-bs-dismiss="PluginName"', () => { + fixtureEl.innerHTML = [ + '<div id="foo" class="test">', + ' <button type="button" data-bs-dismiss="test" data-bs-target="#foo"></button>', + '</div>' + ].join('') + + spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough() + spyOn(DummyClass2.prototype, 'testMethod') + const componentWrapper = fixtureEl.querySelector('#foo') + const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') + const event = createEvent('click') + + enableDismissTrigger(DummyClass2, 'testMethod') + btnClose.dispatchEvent(event) + + expect(DummyClass2.getOrCreateInstance).toHaveBeenCalledWith(componentWrapper) + expect(DummyClass2.prototype.testMethod).toHaveBeenCalled() + }) + + it('if data-bs-dismiss="PluginName" hasn\'t got "data-bs-target", "getOrCreateInstance" has to be initialized by closest "plugin.Name" class', () => { + fixtureEl.innerHTML = [ + '<div id="foo" class="test">', + ' <button type="button" data-bs-dismiss="test"></button>', + '</div>' + ].join('') + + spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough() + spyOn(DummyClass2.prototype, 'hide') + const componentWrapper = fixtureEl.querySelector('#foo') + const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') + const event = createEvent('click') + + enableDismissTrigger(DummyClass2) + btnClose.dispatchEvent(event) + + expect(DummyClass2.getOrCreateInstance).toHaveBeenCalledWith(componentWrapper) + expect(DummyClass2.prototype.hide).toHaveBeenCalled() + }) + + it('if data-bs-dismiss="PluginName" is disabled, must not trigger function', () => { + fixtureEl.innerHTML = [ + '<div id="foo" class="test">', + ' <button type="button" disabled data-bs-dismiss="test"></button>', + '</div>' + ].join('') + + spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough() + const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') + const event = createEvent('click') + + enableDismissTrigger(DummyClass2) + btnClose.dispatchEvent(event) + + expect(DummyClass2.getOrCreateInstance).not.toHaveBeenCalled() + }) + + it('should prevent default when the trigger is <a> or <area>', () => { + fixtureEl.innerHTML = [ + '<div id="foo" class="test">', + ' <a type="button" data-bs-dismiss="test"></a>', + '</div>' + ].join('') + + const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]') + const event = createEvent('click') + + enableDismissTrigger(DummyClass2) + spyOn(Event.prototype, 'preventDefault').and.callThrough() + + btnClose.dispatchEvent(event) + + expect(Event.prototype.preventDefault).toHaveBeenCalled() + }) + }) +}) diff --git a/js/tests/unit/util/focustrap.spec.js b/js/tests/unit/util/focustrap.spec.js new file mode 100644 index 000000000..99bc95fca --- /dev/null +++ b/js/tests/unit/util/focustrap.spec.js @@ -0,0 +1,210 @@ +import FocusTrap from '../../../src/util/focustrap' +import EventHandler from '../../../src/dom/event-handler' +import SelectorEngine from '../../../src/dom/selector-engine' +import { clearFixture, getFixture, createEvent } from '../../helpers/fixture' + +describe('FocusTrap', () => { + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('activate', () => { + it('should autofocus itself by default', () => { + fixtureEl.innerHTML = '<div id="focustrap" tabindex="-1"></div>' + + const trapElement = fixtureEl.querySelector('div') + + spyOn(trapElement, 'focus') + + const focustrap = new FocusTrap({ trapElement }) + focustrap.activate() + + expect(trapElement.focus).toHaveBeenCalled() + }) + + it('if configured not to autofocus, should not autofocus itself', () => { + fixtureEl.innerHTML = '<div id="focustrap" tabindex="-1"></div>' + + const trapElement = fixtureEl.querySelector('div') + + spyOn(trapElement, 'focus') + + const focustrap = new FocusTrap({ trapElement, autofocus: false }) + focustrap.activate() + + expect(trapElement.focus).not.toHaveBeenCalled() + }) + + it('should force focus inside focus trap if it can', done => { + fixtureEl.innerHTML = [ + '<a href="#" id="outside">outside</a>', + '<div id="focustrap" tabindex="-1">', + ' <a href="#" id="inside">inside</a>', + '</div>' + ].join('') + + const trapElement = fixtureEl.querySelector('div') + const focustrap = new FocusTrap({ trapElement }) + focustrap.activate() + + const inside = document.getElementById('inside') + + const focusInListener = () => { + expect(inside.focus).toHaveBeenCalled() + document.removeEventListener('focusin', focusInListener) + done() + } + + spyOn(inside, 'focus') + spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside]) + + document.addEventListener('focusin', focusInListener) + + const focusInEvent = createEvent('focusin', { bubbles: true }) + Object.defineProperty(focusInEvent, 'target', { + value: document.getElementById('outside') + }) + + document.dispatchEvent(focusInEvent) + }) + + it('should wrap focus around forward on tab', done => { + fixtureEl.innerHTML = [ + '<a href="#" id="outside">outside</a>', + '<div id="focustrap" tabindex="-1">', + ' <a href="#" id="first">first</a>', + ' <a href="#" id="inside">inside</a>', + ' <a href="#" id="last">last</a>', + '</div>' + ].join('') + + const trapElement = fixtureEl.querySelector('div') + const focustrap = new FocusTrap({ trapElement }) + focustrap.activate() + + const first = document.getElementById('first') + const inside = document.getElementById('inside') + const last = document.getElementById('last') + const outside = document.getElementById('outside') + + spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last]) + spyOn(first, 'focus').and.callThrough() + + const focusInListener = () => { + expect(first.focus).toHaveBeenCalled() + first.removeEventListener('focusin', focusInListener) + done() + } + + first.addEventListener('focusin', focusInListener) + + const keydown = createEvent('keydown') + keydown.key = 'Tab' + + document.dispatchEvent(keydown) + outside.focus() + }) + + it('should wrap focus around backwards on shift-tab', done => { + fixtureEl.innerHTML = [ + '<a href="#" id="outside">outside</a>', + '<div id="focustrap" tabindex="-1">', + ' <a href="#" id="first">first</a>', + ' <a href="#" id="inside">inside</a>', + ' <a href="#" id="last">last</a>', + '</div>' + ].join('') + + const trapElement = fixtureEl.querySelector('div') + const focustrap = new FocusTrap({ trapElement }) + focustrap.activate() + + const first = document.getElementById('first') + const inside = document.getElementById('inside') + const last = document.getElementById('last') + const outside = document.getElementById('outside') + + spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last]) + spyOn(last, 'focus').and.callThrough() + + const focusInListener = () => { + expect(last.focus).toHaveBeenCalled() + last.removeEventListener('focusin', focusInListener) + done() + } + + last.addEventListener('focusin', focusInListener) + + const keydown = createEvent('keydown') + keydown.key = 'Tab' + keydown.shiftKey = true + + document.dispatchEvent(keydown) + outside.focus() + }) + + it('should force focus on itself if there is no focusable content', done => { + fixtureEl.innerHTML = [ + '<a href="#" id="outside">outside</a>', + '<div id="focustrap" tabindex="-1"></div>' + ].join('') + + const trapElement = fixtureEl.querySelector('div') + const focustrap = new FocusTrap({ trapElement }) + focustrap.activate() + + const focusInListener = () => { + expect(focustrap._config.trapElement.focus).toHaveBeenCalled() + document.removeEventListener('focusin', focusInListener) + done() + } + + spyOn(focustrap._config.trapElement, 'focus') + + document.addEventListener('focusin', focusInListener) + + const focusInEvent = createEvent('focusin', { bubbles: true }) + Object.defineProperty(focusInEvent, 'target', { + value: document.getElementById('outside') + }) + + document.dispatchEvent(focusInEvent) + }) + }) + + describe('deactivate', () => { + it('should flag itself as no longer active', () => { + const focustrap = new FocusTrap({ trapElement: fixtureEl }) + focustrap.activate() + expect(focustrap._isActive).toBe(true) + + focustrap.deactivate() + expect(focustrap._isActive).toBe(false) + }) + + it('should remove all event listeners', () => { + const focustrap = new FocusTrap({ trapElement: fixtureEl }) + focustrap.activate() + + spyOn(EventHandler, 'off') + focustrap.deactivate() + + expect(EventHandler.off).toHaveBeenCalled() + }) + + it('doesn\'t try removing event listeners unless it needs to (in case it hasn\'t been activated)', () => { + const focustrap = new FocusTrap({ trapElement: fixtureEl }) + + spyOn(EventHandler, 'off') + focustrap.deactivate() + + expect(EventHandler.off).not.toHaveBeenCalled() + }) + }) +}) diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index 9b5d7b70e..38e94dc6b 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -543,8 +543,9 @@ describe('Util', () => { fixtureEl.innerHTML = '<div></div>' const div = fixtureEl.querySelector('div') - - expect(Util.reflow(div)).toEqual(0) + const spy = spyOnProperty(div, 'offsetHeight') + Util.reflow(div) + expect(spy).toHaveBeenCalled() }) }) |
