diff options
| author | alpadev <[email protected]> | 2021-06-03 13:44:16 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-06-03 14:44:16 +0300 |
| commit | 4a5029ea29ac75243dfec68153051292fc70f5cf (patch) | |
| tree | 7d0a74707b0f1ab8254610a53dcbf92eabae81b4 /js/tests | |
| parent | 071a288d39f70719c771cd56ded6c36b5d150a83 (diff) | |
| download | bootstrap-4a5029ea29ac75243dfec68153051292fc70f5cf.tar.xz bootstrap-4a5029ea29ac75243dfec68153051292fc70f5cf.zip | |
Fix handling of transitionend events dispatched by nested elements(#33845)
Fix handling of transitionend events dispatched by nested elements
Properly handle events from nested elements
Change `emulateTransitionEnd` to `executeAfterTransition` &&
Diffstat (limited to 'js/tests')
| -rw-r--r-- | js/tests/unit/modal.spec.js | 23 | ||||
| -rw-r--r-- | js/tests/unit/tooltip.spec.js | 2 | ||||
| -rw-r--r-- | js/tests/unit/util/index.spec.js | 153 |
3 files changed, 148 insertions, 30 deletions
diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js index f73ac40b5..2974495ca 100644 --- a/js/tests/unit/modal.spec.js +++ b/js/tests/unit/modal.spec.js @@ -539,6 +539,29 @@ describe('Modal', () => { modal.show() }) + it('should not queue multiple callbacks when clicking outside of modal-content and backdrop = static', done => { + fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 50ms;"></div></div>' + + const modalEl = fixtureEl.querySelector('.modal') + const modal = new Modal(modalEl, { + backdrop: 'static' + }) + + modalEl.addEventListener('shown.bs.modal', () => { + const spy = spyOn(modal, '_queueCallback').and.callThrough() + + modalEl.click() + modalEl.click() + + setTimeout(() => { + expect(spy).toHaveBeenCalledTimes(1) + done() + }, 20) + }) + + modal.show() + }) + it('should enforce focus', done => { fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js index 27c7a350b..b5a34bfe0 100644 --- a/js/tests/unit/tooltip.spec.js +++ b/js/tests/unit/tooltip.spec.js @@ -446,7 +446,7 @@ describe('Tooltip', () => { const tooltip = new Tooltip(tooltipEl) document.documentElement.ontouchstart = noop - spyOn(EventHandler, 'on') + spyOn(EventHandler, 'on').and.callThrough() tooltipEl.addEventListener('shown.bs.tooltip', () => { expect(document.querySelector('.tooltip')).not.toBeNull() diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index 737ecacfd..04ad6bf43 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -157,12 +157,13 @@ describe('Util', () => { describe('triggerTransitionEnd', () => { it('should trigger transitionend event', done => { - fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>' + fixtureEl.innerHTML = '<div></div>' const el = fixtureEl.querySelector('div') + const spy = spyOn(el, 'dispatchEvent').and.callThrough() el.addEventListener('transitionend', () => { - expect().nothing() + expect(spy).toHaveBeenCalled() done() }) @@ -226,33 +227,6 @@ describe('Util', () => { }) }) - describe('emulateTransitionEnd', () => { - it('should emulate transition end', () => { - fixtureEl.innerHTML = '<div></div>' - - const el = document.querySelector('div') - const spy = spyOn(window, 'setTimeout') - - Util.emulateTransitionEnd(el, 10) - expect(spy).toHaveBeenCalled() - }) - - it('should not emulate transition end if already triggered', done => { - fixtureEl.innerHTML = '<div></div>' - - const el = fixtureEl.querySelector('div') - const spy = spyOn(el, 'removeEventListener') - - Util.emulateTransitionEnd(el, 10) - Util.triggerTransitionEnd(el) - - setTimeout(() => { - expect(spy).toHaveBeenCalled() - done() - }, 20) - }) - }) - describe('typeCheckConfig', () => { const namePlugin = 'collapse' @@ -660,6 +634,127 @@ describe('Util', () => { }) }) + describe('executeAfterTransition', () => { + it('should immediately execute a function when waitForTransition parameter is false', () => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + const eventListenerSpy = spyOn(el, 'addEventListener') + + Util.executeAfterTransition(callbackSpy, el, false) + + expect(callbackSpy).toHaveBeenCalled() + expect(eventListenerSpy).not.toHaveBeenCalled() + }) + + it('should execute a function when a transitionend event is dispatched', () => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + el.dispatchEvent(new TransitionEvent('transitionend')) + + expect(callbackSpy).toHaveBeenCalled() + }) + + it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', done => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + setTimeout(() => { + expect(callbackSpy).toHaveBeenCalled() + done() + }, 70) + }) + + 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 => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + setTimeout(() => { + el.dispatchEvent(new TransitionEvent('transitionend')) + }, 50) + + setTimeout(() => { + expect(callbackSpy).toHaveBeenCalledTimes(1) + done() + }, 70) + }) + + it('should not trigger a transitionend event if another transitionend event had already happened', done => { + const el = document.createElement('div') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(() => {}, el) + + // simulate a event dispatched by the browser + el.dispatchEvent(new TransitionEvent('transitionend')) + + const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough() + + setTimeout(() => { + // setTimeout should not have triggered another transitionend event. + expect(dispatchSpy).not.toHaveBeenCalled() + done() + }, 70) + }) + + it('should ignore transitionend events from nested elements', done => { + fixtureEl.innerHTML = [ + '<div class="outer">', + ' <div class="nested"></div>', + '</div>' + ].join('') + + const outer = fixtureEl.querySelector('.outer') + const nested = fixtureEl.querySelector('.nested') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, outer) + + nested.dispatchEvent(new TransitionEvent('transitionend', { + bubbles: true + })) + + setTimeout(() => { + expect(callbackSpy).not.toHaveBeenCalled() + }, 20) + + setTimeout(() => { + expect(callbackSpy).toHaveBeenCalled() + done() + }, 70) + }) + }) + describe('getNextActiveElement', () => { it('should return first element if active not exists or not given and shouldGetNext is either true, or false with cycling being disabled', () => { const array = ['a', 'b', 'c', 'd'] |
