diff options
| author | XhmikosR <[email protected]> | 2021-07-29 09:14:40 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-07-29 09:14:40 +0300 |
| commit | ef5336373fc2431b3d1d37cde85cd262210a1dc6 (patch) | |
| tree | e325fb4c5532b464d05780c731d0f118f2a88d7f /js/tests/unit/util | |
| parent | 62edf07d7491684fe67a9c1e9439ed2bd10ca741 (diff) | |
| parent | c6c0bbb0b67fe89b55740a63fd10d4ad79044970 (diff) | |
| download | bootstrap-main-fod-simpler-table-structure.tar.xz bootstrap-main-fod-simpler-table-structure.zip | |
Merge branch 'main' into main-fod-simpler-table-structuremain-fod-simpler-table-structure
Diffstat (limited to 'js/tests/unit/util')
| -rw-r--r-- | js/tests/unit/util/backdrop.spec.js | 84 | ||||
| -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 | 18 |
4 files changed, 382 insertions, 38 deletions
diff --git a/js/tests/unit/util/backdrop.spec.js b/js/tests/unit/util/backdrop.spec.js index 3150ba14d..59b789071 100644 --- a/js/tests/unit/util/backdrop.spec.js +++ b/js/tests/unit/util/backdrop.spec.js @@ -230,46 +230,62 @@ describe('Backdrop', () => { }) }) }) - - describe('rootElement initialization', () => { - it('Should be appended on "document.body" by default', done => { - const instance = new Backdrop({ - isVisible: true - }) - const getElement = () => document.querySelector(CLASS_BACKDROP) - instance.show(() => { - expect(getElement().parentElement).toEqual(document.body) - done() + describe('Config', () => { + describe('rootElement initialization', () => { + it('Should be appended on "document.body" by default', done => { + const instance = new Backdrop({ + isVisible: true + }) + const getElement = () => document.querySelector(CLASS_BACKDROP) + instance.show(() => { + expect(getElement().parentElement).toEqual(document.body) + done() + }) }) - }) - it('Should find the rootElement if passed as a string', done => { - const instance = new Backdrop({ - isVisible: true, - rootElement: 'body' - }) - const getElement = () => document.querySelector(CLASS_BACKDROP) - instance.show(() => { - expect(getElement().parentElement).toEqual(document.body) - done() + it('Should find the rootElement if passed as a string', done => { + const instance = new Backdrop({ + isVisible: true, + rootElement: 'body' + }) + const getElement = () => document.querySelector(CLASS_BACKDROP) + instance.show(() => { + expect(getElement().parentElement).toEqual(document.body) + done() + }) }) - }) - it('Should appended on any element given by the proper config', done => { - fixtureEl.innerHTML = [ - '<div id="wrapper">', - '</div>' - ].join('') + it('Should appended on any element given by the proper config', done => { + fixtureEl.innerHTML = [ + '<div id="wrapper">', + '</div>' + ].join('') - const wrapper = fixtureEl.querySelector('#wrapper') - const instance = new Backdrop({ - isVisible: true, - rootElement: wrapper + const wrapper = fixtureEl.querySelector('#wrapper') + const instance = new Backdrop({ + isVisible: true, + rootElement: wrapper + }) + const getElement = () => document.querySelector(CLASS_BACKDROP) + instance.show(() => { + expect(getElement().parentElement).toEqual(wrapper) + done() + }) }) - const getElement = () => document.querySelector(CLASS_BACKDROP) - instance.show(() => { - expect(getElement().parentElement).toEqual(wrapper) - done() + }) + + describe('ClassName', () => { + it('Should be able to have different classNames than default', done => { + const instance = new Backdrop({ + isVisible: true, + className: 'foo' + }) + const getElement = () => document.querySelector('.foo') + instance.show(() => { + expect(getElement()).toEqual(instance._getElement()) + instance.dispose() + 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..2457239c4 --- /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 foward 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 04ad6bf43..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() }) }) @@ -582,15 +583,24 @@ describe('Util', () => { }) describe('onDOMContentLoaded', () => { - it('should execute callback when DOMContentLoaded is fired', () => { + it('should execute callbacks when DOMContentLoaded is fired and should not add more than one listener', () => { const spy = jasmine.createSpy() + const spy2 = jasmine.createSpy() + + spyOn(document, 'addEventListener').and.callThrough() spyOnProperty(document, 'readyState').and.returnValue('loading') + Util.onDOMContentLoaded(spy) - window.document.dispatchEvent(new Event('DOMContentLoaded', { + Util.onDOMContentLoaded(spy2) + + document.dispatchEvent(new Event('DOMContentLoaded', { bubbles: true, cancelable: true })) + expect(spy).toHaveBeenCalled() + expect(spy2).toHaveBeenCalled() + expect(document.addEventListener).toHaveBeenCalledTimes(1) }) it('should execute callback if readyState is not "loading"', () => { |
