]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
tests: replace 'done' callback with 'Promise' to fix deprecation errors (#35659)
authorGeoSot <geo.sotis@gmail.com>
Sun, 30 Jan 2022 12:30:04 +0000 (14:30 +0200)
committerGitHub <noreply@github.com>
Sun, 30 Jan 2022 12:30:04 +0000 (14:30 +0200)
Reference:

https://jasmine.github.io/tutorials/async

'DEPRECATION: An asynchronous function called its 'done' callback more than once. This is a bug in the spec, beforeAll, beforeEach, afterAll, or afterEach function in question. This will be treated as an error in a future version. See<https://jasmine.github.io/tutorials/upgrading_to_Jasmine_4.0#deprecations-due-to-calling-done-multiple-times> for more information.

20 files changed:
js/tests/README.md
js/tests/unit/alert.spec.js
js/tests/unit/carousel.spec.js
js/tests/unit/collapse.spec.js
js/tests/unit/dom/event-handler.spec.js
js/tests/unit/dom/manipulator.spec.js
js/tests/unit/dropdown.spec.js
js/tests/unit/jquery.spec.js
js/tests/unit/modal.spec.js
js/tests/unit/offcanvas.spec.js
js/tests/unit/popover.spec.js
js/tests/unit/scrollspy.spec.js
js/tests/unit/tab.spec.js
js/tests/unit/toast.spec.js
js/tests/unit/tooltip.spec.js
js/tests/unit/util/backdrop.spec.js
js/tests/unit/util/focustrap.spec.js
js/tests/unit/util/index.spec.js
js/tests/unit/util/scrollbar.spec.js
js/tests/unit/util/swipe.spec.js

index ca99c0ede032fc127f144555fce40bff38393fc9..79d05d444f6eead278586bbd34ac0c5a4127a64f 100644 (file)
@@ -50,22 +50,24 @@ describe('getInstance', () => {
 })
 
 // Asynchronous test
-it('should show a tooltip without the animation', done => {
-  fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+it('should show a tooltip without the animation', () => {
+  return new Promise(resolve => {
+    fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
 
-  const tooltipEl = fixtureEl.querySelector('a')
-  const tooltip = new Tooltip(tooltipEl, {
-    animation: false
-  })
+    const tooltipEl = fixtureEl.querySelector('a')
+    const tooltip = new Tooltip(tooltipEl, {
+      animation: false
+    })
 
-  tooltipEl.addEventListener('shown.bs.tooltip', () => {
-    const tip = document.querySelector('.tooltip')
+    tooltipEl.addEventListener('shown.bs.tooltip', () => {
+      const tip = document.querySelector('.tooltip')
 
-    expect(tip).not.toBeNull()
-    expect(tip.classList.contains('fade')).toEqual(false)
-    done()
-  })
+      expect(tip).not.toBeNull()
+      expect(tip.classList.contains('fade')).toEqual(false)
+      resolve()
+    })
 
-  tooltip.show()
+    tooltip.show()
+  })
 })
 ```
index 210ae9a25eeff4871458f5e905e0e74448f982d6..e2fe49246a8972baef2cb2f9e4d0614aec6427c5 100644 (file)
@@ -63,60 +63,66 @@ describe('Alert', () => {
   })
 
   describe('close', () => {
-    it('should close an alert', done => {
-      const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
-      fixtureEl.innerHTML = '<div class="alert"></div>'
+    it('should close an alert', () => {
+      return new Promise(resolve => {
+        const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+        fixtureEl.innerHTML = '<div class="alert"></div>'
 
-      const alertEl = document.querySelector('.alert')
-      const alert = new Alert(alertEl)
+        const alertEl = document.querySelector('.alert')
+        const alert = new Alert(alertEl)
 
-      alertEl.addEventListener('closed.bs.alert', () => {
-        expect(document.querySelectorAll('.alert')).toHaveSize(0)
-        expect(spy).not.toHaveBeenCalled()
-        done()
-      })
+        alertEl.addEventListener('closed.bs.alert', () => {
+          expect(document.querySelectorAll('.alert')).toHaveSize(0)
+          expect(spy).not.toHaveBeenCalled()
+          resolve()
+        })
 
-      alert.close()
+        alert.close()
+      })
     })
 
-    it('should close alert with fade class', done => {
-      fixtureEl.innerHTML = '<div class="alert fade"></div>'
+    it('should close alert with fade class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="alert fade"></div>'
 
-      const alertEl = document.querySelector('.alert')
-      const alert = new Alert(alertEl)
+        const alertEl = document.querySelector('.alert')
+        const alert = new Alert(alertEl)
 
-      alertEl.addEventListener('transitionend', () => {
-        expect().nothing()
-      })
+        alertEl.addEventListener('transitionend', () => {
+          expect().nothing()
+        })
 
-      alertEl.addEventListener('closed.bs.alert', () => {
-        expect(document.querySelectorAll('.alert')).toHaveSize(0)
-        done()
-      })
+        alertEl.addEventListener('closed.bs.alert', () => {
+          expect(document.querySelectorAll('.alert')).toHaveSize(0)
+          resolve()
+        })
 
-      alert.close()
+        alert.close()
+      })
     })
 
-    it('should not remove alert if close event is prevented', done => {
-      fixtureEl.innerHTML = '<div class="alert"></div>'
+    it('should not remove alert if close event is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="alert"></div>'
 
-      const getAlert = () => document.querySelector('.alert')
-      const alertEl = getAlert()
-      const alert = new Alert(alertEl)
+        const getAlert = () => document.querySelector('.alert')
+        const alertEl = getAlert()
+        const alert = new Alert(alertEl)
 
-      alertEl.addEventListener('close.bs.alert', event => {
-        event.preventDefault()
-        setTimeout(() => {
-          expect(getAlert()).not.toBeNull()
-          done()
-        }, 10)
-      })
+        alertEl.addEventListener('close.bs.alert', event => {
+          event.preventDefault()
+          setTimeout(() => {
+            expect(getAlert()).not.toBeNull()
+            resolve()
+          }, 10)
+        })
 
-      alertEl.addEventListener('closed.bs.alert', () => {
-        throw new Error('should not fire closed event')
-      })
+        alertEl.addEventListener('closed.bs.alert', () => {
+          throw new Error('should not fire closed event')
+        })
 
-      alert.close()
+        alert.close()
+      })
     })
   })
 
index 7b58b9de9eac6000387115057545955a94776dbe..1c91cebec4c92627eeb1694cca039e945cdecbbc 100644 (file)
@@ -63,94 +63,100 @@ describe('Carousel', () => {
       expect(carouselByElement._element).toEqual(carouselEl)
     })
 
-    it('should go to next item if right arrow key is pressed', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div id="item2" class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should go to next item if right arrow key is pressed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div id="item2" class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {
+          keyboard: true
+        })
+
+        spyOn(carousel, '_keydown').and.callThrough()
+
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
+          expect(carousel._keydown).toHaveBeenCalled()
+          resolve()
+        })
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {
-        keyboard: true
-      })
-
-      spyOn(carousel, '_keydown').and.callThrough()
+        const keydown = createEvent('keydown')
+        keydown.key = 'ArrowRight'
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
-        expect(carousel._keydown).toHaveBeenCalled()
-        done()
+        carouselEl.dispatchEvent(keydown)
       })
-
-      const keydown = createEvent('keydown')
-      keydown.key = 'ArrowRight'
-
-      carouselEl.dispatchEvent(keydown)
     })
 
-    it('should go to previous item if left arrow key is pressed', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div id="item1" class="carousel-item">item 1</div>',
-        '    <div class="carousel-item active">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should go to previous item if left arrow key is pressed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div id="item1" class="carousel-item">item 1</div>',
+          '    <div class="carousel-item active">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {
-        keyboard: true
-      })
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {
+          keyboard: true
+        })
 
-      spyOn(carousel, '_keydown').and.callThrough()
+        spyOn(carousel, '_keydown').and.callThrough()
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
-        expect(carousel._keydown).toHaveBeenCalled()
-        done()
-      })
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
+          expect(carousel._keydown).toHaveBeenCalled()
+          resolve()
+        })
 
-      const keydown = createEvent('keydown')
-      keydown.key = 'ArrowLeft'
+        const keydown = createEvent('keydown')
+        keydown.key = 'ArrowLeft'
 
-      carouselEl.dispatchEvent(keydown)
+        carouselEl.dispatchEvent(keydown)
+      })
     })
 
-    it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {
-        keyboard: true
-      })
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {
+          keyboard: true
+        })
 
-      spyOn(carousel, '_keydown').and.callThrough()
+        spyOn(carousel, '_keydown').and.callThrough()
 
-      carouselEl.addEventListener('keydown', event => {
-        expect(carousel._keydown).toHaveBeenCalled()
-        expect(event.defaultPrevented).toBeFalse()
-        done()
-      })
+        carouselEl.addEventListener('keydown', event => {
+          expect(carousel._keydown).toHaveBeenCalled()
+          expect(event.defaultPrevented).toBeFalse()
+          resolve()
+        })
 
-      const keydown = createEvent('keydown')
-      keydown.key = 'ArrowDown'
+        const keydown = createEvent('keydown')
+        keydown.key = 'ArrowDown'
 
-      carouselEl.dispatchEvent(keydown)
+        carouselEl.dispatchEvent(keydown)
+      })
     })
 
     it('should ignore keyboard events within <input>s and <textarea>s', () => {
@@ -222,70 +228,74 @@ describe('Carousel', () => {
       expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
     })
 
-    it('should wrap around from end to start when wrap option is true', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div id="one" class="carousel-item active"></div>',
-        '    <div id="two" class="carousel-item"></div>',
-        '    <div id="three" class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, { wrap: true })
-      const getActiveId = () => carouselEl.querySelector('.carousel-item.active').getAttribute('id')
-
-      carouselEl.addEventListener('slid.bs.carousel', event => {
-        const activeId = getActiveId()
-
-        if (activeId === 'two') {
-          carousel.next()
-          return
-        }
-
-        if (activeId === 'three') {
-          carousel.next()
-          return
-        }
-
-        if (activeId === 'one') {
-          // carousel wrapped around and slid from 3rd to 1st slide
-          expect(activeId).toEqual('one')
-          expect(event.from + 1).toEqual(3)
-          done()
-        }
+    it('should wrap around from end to start when wrap option is true', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div id="one" class="carousel-item active"></div>',
+          '    <div id="two" class="carousel-item"></div>',
+          '    <div id="three" class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, { wrap: true })
+        const getActiveId = () => carouselEl.querySelector('.carousel-item.active').getAttribute('id')
+
+        carouselEl.addEventListener('slid.bs.carousel', event => {
+          const activeId = getActiveId()
+
+          if (activeId === 'two') {
+            carousel.next()
+            return
+          }
+
+          if (activeId === 'three') {
+            carousel.next()
+            return
+          }
+
+          if (activeId === 'one') {
+            // carousel wrapped around and slid from 3rd to 1st slide
+            expect(activeId).toEqual('one')
+            expect(event.from + 1).toEqual(3)
+            resolve()
+          }
+        })
+
+        carousel.next()
       })
-
-      carousel.next()
     })
 
-    it('should stay at the start when the prev method is called and wrap is false', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div id="one" class="carousel-item active"></div>',
-        '    <div id="two" class="carousel-item"></div>',
-        '    <div id="three" class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should stay at the start when the prev method is called and wrap is false', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div id="one" class="carousel-item active"></div>',
+          '    <div id="two" class="carousel-item"></div>',
+          '    <div id="three" class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const firstElement = fixtureEl.querySelector('#one')
-      const carousel = new Carousel(carouselEl, { wrap: false })
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const firstElement = fixtureEl.querySelector('#one')
+        const carousel = new Carousel(carouselEl, { wrap: false })
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        throw new Error('carousel slid when it should not have slid')
-      })
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          throw new Error('carousel slid when it should not have slid')
+        })
 
-      carousel.prev()
+        carousel.prev()
 
-      setTimeout(() => {
-        expect(firstElement).toHaveClass('active')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(firstElement).toHaveClass('active')
+          resolve()
+        }, 10)
+      })
     })
 
     it('should not add touch event listeners if touch = false', () => {
@@ -335,274 +345,290 @@ describe('Carousel', () => {
       expect(carousel._addTouchEventListeners).toHaveBeenCalled()
     })
 
-    it('should allow swiperight and call _slide (prev) with pointer events', done => {
-      if (!supportPointerEvent) {
-        expect().nothing()
-        done()
-        return
-      }
-
-      document.documentElement.ontouchstart = noop
-      document.head.append(stylesCarousel)
-      Simulator.setType('pointer')
-
-      fixtureEl.innerHTML = [
-        '<div class="carousel" data-bs-interval="false">',
-        '  <div class="carousel-inner">',
-        '    <div id="item" class="carousel-item">',
-        '      <img alt="">',
-        '    </div>',
-        '    <div class="carousel-item active">',
-        '      <img alt="">',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const item = fixtureEl.querySelector('#item')
-      const carousel = new Carousel(carouselEl)
-
-      spyOn(carousel, '_slide').and.callThrough()
-
-      carouselEl.addEventListener('slid.bs.carousel', event => {
-        expect(item).toHaveClass('active')
-        expect(carousel._slide).toHaveBeenCalledWith('right')
-        expect(event.direction).toEqual('right')
-        stylesCarousel.remove()
-        delete document.documentElement.ontouchstart
-        done()
-      })
+    it('should allow swiperight and call _slide (prev) with pointer events', () => {
+      return new Promise(resolve => {
+        if (!supportPointerEvent) {
+          expect().nothing()
+          resolve()
+          return
+        }
 
-      Simulator.gestures.swipe(carouselEl, {
-        deltaX: 300,
-        deltaY: 0
+        document.documentElement.ontouchstart = noop
+        document.head.append(stylesCarousel)
+        Simulator.setType('pointer')
+
+        fixtureEl.innerHTML = [
+          '<div class="carousel" data-bs-interval="false">',
+          '  <div class="carousel-inner">',
+          '    <div id="item" class="carousel-item">',
+          '      <img alt="">',
+          '    </div>',
+          '    <div class="carousel-item active">',
+          '      <img alt="">',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const item = fixtureEl.querySelector('#item')
+        const carousel = new Carousel(carouselEl)
+
+        spyOn(carousel, '_slide').and.callThrough()
+
+        carouselEl.addEventListener('slid.bs.carousel', event => {
+          expect(item).toHaveClass('active')
+          expect(carousel._slide).toHaveBeenCalledWith('right')
+          expect(event.direction).toEqual('right')
+          stylesCarousel.remove()
+          delete document.documentElement.ontouchstart
+          resolve()
+        })
+
+        Simulator.gestures.swipe(carouselEl, {
+          deltaX: 300,
+          deltaY: 0
+        })
       })
     })
 
-    it('should allow swipeleft and call next with pointer events', done => {
-      if (!supportPointerEvent) {
-        expect().nothing()
-        done()
-        return
-      }
-
-      document.documentElement.ontouchstart = noop
-      document.head.append(stylesCarousel)
-      Simulator.setType('pointer')
-
-      fixtureEl.innerHTML = [
-        '<div class="carousel" data-bs-interval="false">',
-        '  <div class="carousel-inner">',
-        '    <div id="item" class="carousel-item active">',
-        '      <img alt="">',
-        '    </div>',
-        '    <div class="carousel-item">',
-        '      <img alt="">',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const item = fixtureEl.querySelector('#item')
-      const carousel = new Carousel(carouselEl)
-
-      spyOn(carousel, '_slide').and.callThrough()
-
-      carouselEl.addEventListener('slid.bs.carousel', event => {
-        expect(item).not.toHaveClass('active')
-        expect(carousel._slide).toHaveBeenCalledWith('left')
-        expect(event.direction).toEqual('left')
-        stylesCarousel.remove()
-        delete document.documentElement.ontouchstart
-        done()
-      })
+    it('should allow swipeleft and call next with pointer events', () => {
+      return new Promise(resolve => {
+        if (!supportPointerEvent) {
+          expect().nothing()
+          resolve()
+          return
+        }
 
-      Simulator.gestures.swipe(carouselEl, {
-        pos: [300, 10],
-        deltaX: -300,
-        deltaY: 0
+        document.documentElement.ontouchstart = noop
+        document.head.append(stylesCarousel)
+        Simulator.setType('pointer')
+
+        fixtureEl.innerHTML = [
+          '<div class="carousel" data-bs-interval="false">',
+          '  <div class="carousel-inner">',
+          '    <div id="item" class="carousel-item active">',
+          '      <img alt="">',
+          '    </div>',
+          '    <div class="carousel-item">',
+          '      <img alt="">',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const item = fixtureEl.querySelector('#item')
+        const carousel = new Carousel(carouselEl)
+
+        spyOn(carousel, '_slide').and.callThrough()
+
+        carouselEl.addEventListener('slid.bs.carousel', event => {
+          expect(item).not.toHaveClass('active')
+          expect(carousel._slide).toHaveBeenCalledWith('left')
+          expect(event.direction).toEqual('left')
+          stylesCarousel.remove()
+          delete document.documentElement.ontouchstart
+          resolve()
+        })
+
+        Simulator.gestures.swipe(carouselEl, {
+          pos: [300, 10],
+          deltaX: -300,
+          deltaY: 0
+        })
       })
     })
 
-    it('should allow swiperight and call _slide (prev) with touch events', done => {
-      Simulator.setType('touch')
-      clearPointerEvents()
-      document.documentElement.ontouchstart = noop
-
-      fixtureEl.innerHTML = [
-        '<div class="carousel" data-bs-interval="false">',
-        '  <div class="carousel-inner">',
-        '    <div id="item" class="carousel-item">',
-        '      <img alt="">',
-        '    </div>',
-        '    <div class="carousel-item active">',
-        '      <img alt="">',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const item = fixtureEl.querySelector('#item')
-      const carousel = new Carousel(carouselEl)
-
-      spyOn(carousel, '_slide').and.callThrough()
-
-      carouselEl.addEventListener('slid.bs.carousel', event => {
-        expect(item).toHaveClass('active')
-        expect(carousel._slide).toHaveBeenCalledWith('right')
-        expect(event.direction).toEqual('right')
-        delete document.documentElement.ontouchstart
-        restorePointerEvents()
-        done()
-      })
-
-      Simulator.gestures.swipe(carouselEl, {
-        deltaX: 300,
-        deltaY: 0
+    it('should allow swiperight and call _slide (prev) with touch events', () => {
+      return new Promise(resolve => {
+        Simulator.setType('touch')
+        clearPointerEvents()
+        document.documentElement.ontouchstart = noop
+
+        fixtureEl.innerHTML = [
+          '<div class="carousel" data-bs-interval="false">',
+          '  <div class="carousel-inner">',
+          '    <div id="item" class="carousel-item">',
+          '      <img alt="">',
+          '    </div>',
+          '    <div class="carousel-item active">',
+          '      <img alt="">',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const item = fixtureEl.querySelector('#item')
+        const carousel = new Carousel(carouselEl)
+
+        spyOn(carousel, '_slide').and.callThrough()
+
+        carouselEl.addEventListener('slid.bs.carousel', event => {
+          expect(item).toHaveClass('active')
+          expect(carousel._slide).toHaveBeenCalledWith('right')
+          expect(event.direction).toEqual('right')
+          delete document.documentElement.ontouchstart
+          restorePointerEvents()
+          resolve()
+        })
+
+        Simulator.gestures.swipe(carouselEl, {
+          deltaX: 300,
+          deltaY: 0
+        })
       })
     })
 
-    it('should allow swipeleft and call _slide (next) with touch events', done => {
-      Simulator.setType('touch')
-      clearPointerEvents()
-      document.documentElement.ontouchstart = noop
-
-      fixtureEl.innerHTML = [
-        '<div class="carousel" data-bs-interval="false">',
-        '  <div class="carousel-inner">',
-        '    <div id="item" class="carousel-item active">',
-        '      <img alt="">',
-        '    </div>',
-        '    <div class="carousel-item">',
-        '      <img alt="">',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const item = fixtureEl.querySelector('#item')
-      const carousel = new Carousel(carouselEl)
-
-      spyOn(carousel, '_slide').and.callThrough()
-
-      carouselEl.addEventListener('slid.bs.carousel', event => {
-        expect(item).not.toHaveClass('active')
-        expect(carousel._slide).toHaveBeenCalledWith('left')
-        expect(event.direction).toEqual('left')
-        delete document.documentElement.ontouchstart
-        restorePointerEvents()
-        done()
-      })
-
-      Simulator.gestures.swipe(carouselEl, {
-        pos: [300, 10],
-        deltaX: -300,
-        deltaY: 0
+    it('should allow swipeleft and call _slide (next) with touch events', () => {
+      return new Promise(resolve => {
+        Simulator.setType('touch')
+        clearPointerEvents()
+        document.documentElement.ontouchstart = noop
+
+        fixtureEl.innerHTML = [
+          '<div class="carousel" data-bs-interval="false">',
+          '  <div class="carousel-inner">',
+          '    <div id="item" class="carousel-item active">',
+          '      <img alt="">',
+          '    </div>',
+          '    <div class="carousel-item">',
+          '      <img alt="">',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const item = fixtureEl.querySelector('#item')
+        const carousel = new Carousel(carouselEl)
+
+        spyOn(carousel, '_slide').and.callThrough()
+
+        carouselEl.addEventListener('slid.bs.carousel', event => {
+          expect(item).not.toHaveClass('active')
+          expect(carousel._slide).toHaveBeenCalledWith('left')
+          expect(event.direction).toEqual('left')
+          delete document.documentElement.ontouchstart
+          restorePointerEvents()
+          resolve()
+        })
+
+        Simulator.gestures.swipe(carouselEl, {
+          pos: [300, 10],
+          deltaX: -300,
+          deltaY: 0
+        })
       })
     })
 
-    it('should not slide when swiping and carousel is sliding', done => {
-      Simulator.setType('touch')
-      clearPointerEvents()
-      document.documentElement.ontouchstart = noop
-
-      fixtureEl.innerHTML = [
-        '<div class="carousel" data-bs-interval="false">',
-        '  <div class="carousel-inner">',
-        '    <div id="item" class="carousel-item active">',
-        '      <img alt="">',
-        '    </div>',
-        '    <div class="carousel-item">',
-        '      <img alt="">',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not slide when swiping and carousel is sliding', () => {
+      return new Promise(resolve => {
+        Simulator.setType('touch')
+        clearPointerEvents()
+        document.documentElement.ontouchstart = noop
+
+        fixtureEl.innerHTML = [
+          '<div class="carousel" data-bs-interval="false">',
+          '  <div class="carousel-inner">',
+          '    <div id="item" class="carousel-item active">',
+          '      <img alt="">',
+          '    </div>',
+          '    <div class="carousel-item">',
+          '      <img alt="">',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const carousel = new Carousel(carouselEl)
+        carousel._isSliding = true
+
+        spyOn(carousel, '_triggerSlideEvent')
+
+        Simulator.gestures.swipe(carouselEl, {
+          deltaX: 300,
+          deltaY: 0
+        })
+
+        Simulator.gestures.swipe(carouselEl, {
+          pos: [300, 10],
+          deltaX: -300,
+          deltaY: 0
+        })
 
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const carousel = new Carousel(carouselEl)
-      carousel._isSliding = true
-
-      spyOn(carousel, '_triggerSlideEvent')
-
-      Simulator.gestures.swipe(carouselEl, {
-        deltaX: 300,
-        deltaY: 0
-      })
-
-      Simulator.gestures.swipe(carouselEl, {
-        pos: [300, 10],
-        deltaX: -300,
-        deltaY: 0
+        setTimeout(() => {
+          expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
+          delete document.documentElement.ontouchstart
+          restorePointerEvents()
+          resolve()
+        }, 300)
       })
-
-      setTimeout(() => {
-        expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
-        delete document.documentElement.ontouchstart
-        restorePointerEvents()
-        done()
-      }, 300)
     })
 
-    it('should not allow pinch with touch events', done => {
-      Simulator.setType('touch')
-      clearPointerEvents()
-      document.documentElement.ontouchstart = noop
-
-      fixtureEl.innerHTML = '<div class="carousel" data-bs-interval="false"></div>'
-
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const carousel = new Carousel(carouselEl)
-
-      Simulator.gestures.swipe(carouselEl, {
-        pos: [300, 10],
-        deltaX: -300,
-        deltaY: 0,
-        touches: 2
-      }, () => {
-        restorePointerEvents()
-        delete document.documentElement.ontouchstart
-        expect(carousel._swipeHelper._deltaX).toEqual(0)
-        done()
+    it('should not allow pinch with touch events', () => {
+      return new Promise(resolve => {
+        Simulator.setType('touch')
+        clearPointerEvents()
+        document.documentElement.ontouchstart = noop
+
+        fixtureEl.innerHTML = '<div class="carousel" data-bs-interval="false"></div>'
+
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const carousel = new Carousel(carouselEl)
+
+        Simulator.gestures.swipe(carouselEl, {
+          pos: [300, 10],
+          deltaX: -300,
+          deltaY: 0,
+          touches: 2
+        }, () => {
+          restorePointerEvents()
+          delete document.documentElement.ontouchstart
+          expect(carousel._swipeHelper._deltaX).toEqual(0)
+          resolve()
+        })
       })
     })
 
-    it('should call pause method on mouse over with pause equal to hover', done => {
-      fixtureEl.innerHTML = '<div class="carousel"></div>'
+    it('should call pause method on mouse over with pause equal to hover', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="carousel"></div>'
 
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const carousel = new Carousel(carouselEl)
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const carousel = new Carousel(carouselEl)
 
-      spyOn(carousel, 'pause')
+        spyOn(carousel, 'pause')
 
-      const mouseOverEvent = createEvent('mouseover')
-      carouselEl.dispatchEvent(mouseOverEvent)
+        const mouseOverEvent = createEvent('mouseover')
+        carouselEl.dispatchEvent(mouseOverEvent)
 
-      setTimeout(() => {
-        expect(carousel.pause).toHaveBeenCalled()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(carousel.pause).toHaveBeenCalled()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should call cycle on mouse out with pause equal to hover', done => {
-      fixtureEl.innerHTML = '<div class="carousel"></div>'
+    it('should call cycle on mouse out with pause equal to hover', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="carousel"></div>'
 
-      const carouselEl = fixtureEl.querySelector('.carousel')
-      const carousel = new Carousel(carouselEl)
+        const carouselEl = fixtureEl.querySelector('.carousel')
+        const carousel = new Carousel(carouselEl)
 
-      spyOn(carousel, 'cycle')
+        spyOn(carousel, 'cycle')
 
-      const mouseOutEvent = createEvent('mouseout')
-      carouselEl.dispatchEvent(mouseOutEvent)
+        const mouseOutEvent = createEvent('mouseout')
+        carouselEl.dispatchEvent(mouseOutEvent)
 
-      setTimeout(() => {
-        expect(carousel.cycle).toHaveBeenCalled()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(carousel.cycle).toHaveBeenCalled()
+          resolve()
+        }, 10)
+      })
     })
   })
 
@@ -621,100 +647,106 @@ describe('Carousel', () => {
       expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
     })
 
-    it('should not fire slid when slide is prevented', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should not fire slid when slide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      const carouselEl = fixtureEl.querySelector('div')
-      const carousel = new Carousel(carouselEl, {})
-      let slidEvent = false
+        const carouselEl = fixtureEl.querySelector('div')
+        const carousel = new Carousel(carouselEl, {})
+        let slidEvent = false
 
-      const doneTest = () => {
-        setTimeout(() => {
-          expect(slidEvent).toBeFalse()
-          done()
-        }, 20)
-      }
+        const doneTest = () => {
+          setTimeout(() => {
+            expect(slidEvent).toBeFalse()
+            resolve()
+          }, 20)
+        }
 
-      carouselEl.addEventListener('slide.bs.carousel', event => {
-        event.preventDefault()
-        doneTest()
-      })
+        carouselEl.addEventListener('slide.bs.carousel', event => {
+          event.preventDefault()
+          doneTest()
+        })
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        slidEvent = true
-      })
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          slidEvent = true
+        })
 
-      carousel.next()
+        carousel.next()
+      })
     })
 
-    it('should fire slide event with: direction, relatedTarget, from and to', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should fire slide event with: direction, relatedTarget, from and to', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {})
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {})
 
-      const onSlide = event => {
-        expect(event.direction).toEqual('left')
-        expect(event.relatedTarget).toHaveClass('carousel-item')
-        expect(event.from).toEqual(0)
-        expect(event.to).toEqual(1)
+        const onSlide = event => {
+          expect(event.direction).toEqual('left')
+          expect(event.relatedTarget).toHaveClass('carousel-item')
+          expect(event.from).toEqual(0)
+          expect(event.to).toEqual(1)
 
-        carouselEl.removeEventListener('slide.bs.carousel', onSlide)
-        carouselEl.addEventListener('slide.bs.carousel', onSlide2)
+          carouselEl.removeEventListener('slide.bs.carousel', onSlide)
+          carouselEl.addEventListener('slide.bs.carousel', onSlide2)
 
-        carousel.prev()
-      }
+          carousel.prev()
+        }
 
-      const onSlide2 = event => {
-        expect(event.direction).toEqual('right')
-        done()
-      }
+        const onSlide2 = event => {
+          expect(event.direction).toEqual('right')
+          resolve()
+        }
 
-      carouselEl.addEventListener('slide.bs.carousel', onSlide)
-      carousel.next()
+        carouselEl.addEventListener('slide.bs.carousel', onSlide)
+        carousel.next()
+      })
     })
 
-    it('should fire slid event with: direction, relatedTarget, from and to', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should fire slid event with: direction, relatedTarget, from and to', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {})
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {})
 
-      const onSlid = event => {
-        expect(event.direction).toEqual('left')
-        expect(event.relatedTarget).toHaveClass('carousel-item')
-        expect(event.from).toEqual(0)
-        expect(event.to).toEqual(1)
+        const onSlid = event => {
+          expect(event.direction).toEqual('left')
+          expect(event.relatedTarget).toHaveClass('carousel-item')
+          expect(event.from).toEqual(0)
+          expect(event.to).toEqual(1)
 
-        carouselEl.removeEventListener('slid.bs.carousel', onSlid)
-        carouselEl.addEventListener('slid.bs.carousel', onSlid2)
+          carouselEl.removeEventListener('slid.bs.carousel', onSlid)
+          carouselEl.addEventListener('slid.bs.carousel', onSlid2)
 
-        carousel.prev()
-      }
+          carousel.prev()
+        }
 
-      const onSlid2 = event => {
-        expect(event.direction).toEqual('right')
-        done()
-      }
+        const onSlid2 = event => {
+          expect(event.direction).toEqual('right')
+          resolve()
+        }
 
-      carouselEl.addEventListener('slid.bs.carousel', onSlid)
-      carousel.next()
+        carouselEl.addEventListener('slid.bs.carousel', onSlid)
+        carousel.next()
+      })
     })
 
     it('should update the active element to the next item before sliding', () => {
@@ -737,36 +769,38 @@ describe('Carousel', () => {
       expect(carousel._activeElement).toEqual(secondItemEl)
     })
 
-    it('should update indicators if present', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-indicators">',
-        '    <button type="button" id="firstIndicator" data-bs-target="myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>',
-        '    <button type="button" id="secondIndicator" data-bs-target="myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>',
-        '    <button type="button" data-bs-target="myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>',
-        '  </div>',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item" data-bs-interval="7">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const firstIndicator = fixtureEl.querySelector('#firstIndicator')
-      const secondIndicator = fixtureEl.querySelector('#secondIndicator')
-      const carousel = new Carousel(carouselEl)
-
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        expect(firstIndicator).not.toHaveClass('active')
-        expect(firstIndicator.hasAttribute('aria-current')).toBeFalse()
-        expect(secondIndicator).toHaveClass('active')
-        expect(secondIndicator.getAttribute('aria-current')).toEqual('true')
-        done()
+    it('should update indicators if present', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-indicators">',
+          '    <button type="button" id="firstIndicator" data-bs-target="myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>',
+          '    <button type="button" id="secondIndicator" data-bs-target="myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>',
+          '    <button type="button" data-bs-target="myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>',
+          '  </div>',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item" data-bs-interval="7">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const firstIndicator = fixtureEl.querySelector('#firstIndicator')
+        const secondIndicator = fixtureEl.querySelector('#secondIndicator')
+        const carousel = new Carousel(carouselEl)
+
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          expect(firstIndicator).not.toHaveClass('active')
+          expect(firstIndicator.hasAttribute('aria-current')).toBeFalse()
+          expect(secondIndicator).toHaveClass('active')
+          expect(secondIndicator.getAttribute('aria-current')).toEqual('true')
+          resolve()
+        })
+
+        carousel.next()
       })
-
-      carousel.next()
     })
 
     it('should call next()/prev() instance methods when clicking the respective direction buttons', () => {
@@ -1018,51 +1052,55 @@ describe('Carousel', () => {
   })
 
   describe('to', () => {
-    it('should go directly to the provided index', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div id="item1" class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item">item 2</div>',
-        '    <div id="item3" class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should go directly to the provided index', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div id="item1" class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item">item 2</div>',
+          '    <div id="item3" class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {})
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {})
-
-      expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
+        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
 
-      carousel.to(2)
+        carousel.to(2)
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
-        done()
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
+          resolve()
+        })
       })
     })
 
-    it('should return to a previous slide if the provided index is lower than the current', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item">item 1</div>',
-        '    <div id="item2" class="carousel-item">item 2</div>',
-        '    <div id="item3" class="carousel-item active">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should return to a previous slide if the provided index is lower than the current', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item">item 1</div>',
+          '    <div id="item2" class="carousel-item">item 2</div>',
+          '    <div id="item3" class="carousel-item active">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {})
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {})
 
-      expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
+        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
 
-      carousel.to(1)
+        carousel.to(1)
 
-      carouselEl.addEventListener('slid.bs.carousel', () => {
-        expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
-        done()
+        carouselEl.addEventListener('slid.bs.carousel', () => {
+          expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
+          resolve()
+        })
       })
     })
 
@@ -1118,36 +1156,38 @@ describe('Carousel', () => {
       expect(carousel.cycle).toHaveBeenCalled()
     })
 
-    it('should wait before performing to if a slide is sliding', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div class="carousel-item" data-bs-interval="7">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should wait before performing to if a slide is sliding', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div class="carousel-item" data-bs-interval="7">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const carouselEl = fixtureEl.querySelector('#myCarousel')
-      const carousel = new Carousel(carouselEl, {})
+        const carouselEl = fixtureEl.querySelector('#myCarousel')
+        const carousel = new Carousel(carouselEl, {})
 
-      spyOn(EventHandler, 'one').and.callThrough()
-      spyOn(carousel, '_slide')
+        spyOn(EventHandler, 'one').and.callThrough()
+        spyOn(carousel, '_slide')
 
-      carousel._isSliding = true
-      carousel.to(1)
+        carousel._isSliding = true
+        carousel.to(1)
 
-      expect(carousel._slide).not.toHaveBeenCalled()
-      expect(EventHandler.one).toHaveBeenCalled()
+        expect(carousel._slide).not.toHaveBeenCalled()
+        expect(EventHandler.one).toHaveBeenCalled()
 
-      spyOn(carousel, 'to')
+        spyOn(carousel, 'to')
 
-      EventHandler.trigger(carouselEl, 'slid.bs.carousel')
+        EventHandler.trigger(carouselEl, 'slid.bs.carousel')
 
-      setTimeout(() => {
-        expect(carousel.to).toHaveBeenCalledWith(1)
-        done()
+        setTimeout(() => {
+          expect(carousel.to).toHaveBeenCalledWith(1)
+          resolve()
+        })
       })
     })
   })
@@ -1421,75 +1461,81 @@ describe('Carousel', () => {
       expect(Carousel.getInstance(carouselEl)).not.toBeNull()
     })
 
-    it('should create carousel and go to the next slide on click (with real button controls)', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div id="item2" class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '  <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
-        '  <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
-        '</div>'
-      ].join('')
+    it('should create carousel and go to the next slide on click (with real button controls)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div id="item2" class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '  <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
+          '  <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
+          '</div>'
+        ].join('')
 
-      const next = fixtureEl.querySelector('#next')
-      const item2 = fixtureEl.querySelector('#item2')
+        const next = fixtureEl.querySelector('#next')
+        const item2 = fixtureEl.querySelector('#item2')
 
-      next.click()
+        next.click()
 
-      setTimeout(() => {
-        expect(item2).toHaveClass('active')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(item2).toHaveClass('active')
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should create carousel and go to the next slide on click (using links as controls)', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div id="item2" class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '  <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></a>',
-        '  <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></a>',
-        '</div>'
-      ].join('')
+    it('should create carousel and go to the next slide on click (using links as controls)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div id="item2" class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '  <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></a>',
+          '  <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></a>',
+          '</div>'
+        ].join('')
 
-      const next = fixtureEl.querySelector('#next')
-      const item2 = fixtureEl.querySelector('#item2')
+        const next = fixtureEl.querySelector('#next')
+        const item2 = fixtureEl.querySelector('#item2')
 
-      next.click()
+        next.click()
 
-      setTimeout(() => {
-        expect(item2).toHaveClass('active')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(item2).toHaveClass('active')
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should create carousel and go to the next slide on click with data-bs-slide-to', done => {
-      fixtureEl.innerHTML = [
-        '<div id="myCarousel" class="carousel slide">',
-        '  <div class="carousel-inner">',
-        '    <div class="carousel-item active">item 1</div>',
-        '    <div id="item2" class="carousel-item">item 2</div>',
-        '    <div class="carousel-item">item 3</div>',
-        '  </div>',
-        '  <div id="next" data-bs-target="#myCarousel" data-bs-slide-to="1"></div>',
-        '</div>'
-      ].join('')
+    it('should create carousel and go to the next slide on click with data-bs-slide-to', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="myCarousel" class="carousel slide">',
+          '  <div class="carousel-inner">',
+          '    <div class="carousel-item active">item 1</div>',
+          '    <div id="item2" class="carousel-item">item 2</div>',
+          '    <div class="carousel-item">item 3</div>',
+          '  </div>',
+          '  <div id="next" data-bs-target="#myCarousel" data-bs-slide-to="1"></div>',
+          '</div>'
+        ].join('')
 
-      const next = fixtureEl.querySelector('#next')
-      const item2 = fixtureEl.querySelector('#item2')
+        const next = fixtureEl.querySelector('#next')
+        const item2 = fixtureEl.querySelector('#item2')
 
-      next.click()
+        next.click()
 
-      setTimeout(() => {
-        expect(item2).toHaveClass('active')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(item2).toHaveClass('active')
+          resolve()
+        }, 10)
+      })
     })
 
     it('should do nothing if no selector on click on arrows', () => {
index 327a68449b393e10fbeb85ac5e79efe2bb9a8f12..e2d2864905edee94c8f878b04525567ce1d2f587 100644 (file)
@@ -134,37 +134,39 @@ describe('Collapse', () => {
       expect(collapse.hide).toHaveBeenCalled()
     })
 
-    it('should find collapse children if they have collapse class too not only data-bs-parent', done => {
-      fixtureEl.innerHTML = [
-        '<div class="my-collapse">',
-        '  <div class="item">',
-        '    <a data-bs-toggle="collapse" href="#">Toggle item 1</a>',
-        '    <div id="collapse1" class="collapse show">Lorem ipsum 1</div>',
-        '  </div>',
-        '  <div class="item">',
-        '    <a id="triggerCollapse2" data-bs-toggle="collapse" href="#">Toggle item 2</a>',
-        '    <div id="collapse2" class="collapse">Lorem ipsum 2</div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const parent = fixtureEl.querySelector('.my-collapse')
-      const collapseEl1 = fixtureEl.querySelector('#collapse1')
-      const collapseEl2 = fixtureEl.querySelector('#collapse2')
-
-      const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
-        .map(el => new Collapse(el, {
-          parent,
-          toggle: false
-        }))
+    it('should find collapse children if they have collapse class too not only data-bs-parent', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="my-collapse">',
+          '  <div class="item">',
+          '    <a data-bs-toggle="collapse" href="#">Toggle item 1</a>',
+          '    <div id="collapse1" class="collapse show">Lorem ipsum 1</div>',
+          '  </div>',
+          '  <div class="item">',
+          '    <a id="triggerCollapse2" data-bs-toggle="collapse" href="#">Toggle item 2</a>',
+          '    <div id="collapse2" class="collapse">Lorem ipsum 2</div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const parent = fixtureEl.querySelector('.my-collapse')
+        const collapseEl1 = fixtureEl.querySelector('#collapse1')
+        const collapseEl2 = fixtureEl.querySelector('#collapse2')
+
+        const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
+          .map(el => new Collapse(el, {
+            parent,
+            toggle: false
+          }))
+
+        collapseEl2.addEventListener('shown.bs.collapse', () => {
+          expect(collapseEl2).toHaveClass('show')
+          expect(collapseEl1).not.toHaveClass('show')
+          resolve()
+        })
 
-      collapseEl2.addEventListener('shown.bs.collapse', () => {
-        expect(collapseEl2).toHaveClass('show')
-        expect(collapseEl1).not.toHaveClass('show')
-        done()
+        collapseList[1].toggle()
       })
-
-      collapseList[1].toggle()
     })
   })
 
@@ -200,203 +202,215 @@ describe('Collapse', () => {
       expect(EventHandler.trigger).not.toHaveBeenCalled()
     })
 
-    it('should show a collapsed element', done => {
-      fixtureEl.innerHTML = '<div class="collapse" style="height: 0px;"></div>'
+    it('should show a collapsed element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="collapse" style="height: 0px;"></div>'
 
-      const collapseEl = fixtureEl.querySelector('div')
-      const collapse = new Collapse(collapseEl, {
-        toggle: false
-      })
+        const collapseEl = fixtureEl.querySelector('div')
+        const collapse = new Collapse(collapseEl, {
+          toggle: false
+        })
 
-      collapseEl.addEventListener('show.bs.collapse', () => {
-        expect(collapseEl.style.height).toEqual('0px')
-      })
-      collapseEl.addEventListener('shown.bs.collapse', () => {
-        expect(collapseEl).toHaveClass('show')
-        expect(collapseEl.style.height).toEqual('')
-        done()
-      })
+        collapseEl.addEventListener('show.bs.collapse', () => {
+          expect(collapseEl.style.height).toEqual('0px')
+        })
+        collapseEl.addEventListener('shown.bs.collapse', () => {
+          expect(collapseEl).toHaveClass('show')
+          expect(collapseEl.style.height).toEqual('')
+          resolve()
+        })
 
-      collapse.show()
+        collapse.show()
+      })
     })
 
-    it('should show a collapsed element on width', done => {
-      fixtureEl.innerHTML = '<div class="collapse collapse-horizontal" style="width: 0px;"></div>'
+    it('should show a collapsed element on width', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="collapse collapse-horizontal" style="width: 0px;"></div>'
 
-      const collapseEl = fixtureEl.querySelector('div')
-      const collapse = new Collapse(collapseEl, {
-        toggle: false
-      })
+        const collapseEl = fixtureEl.querySelector('div')
+        const collapse = new Collapse(collapseEl, {
+          toggle: false
+        })
 
-      collapseEl.addEventListener('show.bs.collapse', () => {
-        expect(collapseEl.style.width).toEqual('0px')
-      })
-      collapseEl.addEventListener('shown.bs.collapse', () => {
-        expect(collapseEl).toHaveClass('show')
-        expect(collapseEl.style.width).toEqual('')
-        done()
-      })
+        collapseEl.addEventListener('show.bs.collapse', () => {
+          expect(collapseEl.style.width).toEqual('0px')
+        })
+        collapseEl.addEventListener('shown.bs.collapse', () => {
+          expect(collapseEl).toHaveClass('show')
+          expect(collapseEl.style.width).toEqual('')
+          resolve()
+        })
 
-      collapse.show()
+        collapse.show()
+      })
     })
 
-    it('should collapse only the first collapse', done => {
-      fixtureEl.innerHTML = [
-        '<div class="card" id="accordion1">',
-        '  <div id="collapse1" class="collapse"></div>',
-        '</div>',
-        '<div class="card" id="accordion2">',
-        '  <div id="collapse2" class="collapse show"></div>',
-        '</div>'
-      ].join('')
+    it('should collapse only the first collapse', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="card" id="accordion1">',
+          '  <div id="collapse1" class="collapse"></div>',
+          '</div>',
+          '<div class="card" id="accordion2">',
+          '  <div id="collapse2" class="collapse show"></div>',
+          '</div>'
+        ].join('')
+
+        const el1 = fixtureEl.querySelector('#collapse1')
+        const el2 = fixtureEl.querySelector('#collapse2')
+        const collapse = new Collapse(el1, {
+          toggle: false
+        })
 
-      const el1 = fixtureEl.querySelector('#collapse1')
-      const el2 = fixtureEl.querySelector('#collapse2')
-      const collapse = new Collapse(el1, {
-        toggle: false
-      })
+        el1.addEventListener('shown.bs.collapse', () => {
+          expect(el1).toHaveClass('show')
+          expect(el2).toHaveClass('show')
+          resolve()
+        })
 
-      el1.addEventListener('shown.bs.collapse', () => {
-        expect(el1).toHaveClass('show')
-        expect(el2).toHaveClass('show')
-        done()
+        collapse.show()
       })
-
-      collapse.show()
     })
 
-    it('should be able to handle toggling of other children siblings', done => {
-      fixtureEl.innerHTML = [
-        '<div id="parentGroup" class="accordion">',
-        '  <div id="parentHeader" class="accordion-header">',
-        '    <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
-        '  </div>',
-        '  <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
-        '    <div class="accordion-body">',
-        '      <div id="childGroup" class="accordion">',
-        '        <div class="accordion-item">',
-        '          <div id="childHeader1" class="accordion-header">',
-        '            <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
-        '          </div>',
-        '          <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
-        '            <div>content</div>',
-        '          </div>',
-        '        </div>',
-        '        <div class="accordion-item">',
-        '          <div id="childHeader2" class="accordion-header">',
-        '            <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
-        '          </div>',
-        '          <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
-        '            <div>content</div>',
-        '          </div>',
-        '        </div>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const el = selector => fixtureEl.querySelector(selector)
-
-      const parentBtn = el('[data-bs-target="#parentContent"]')
-      const childBtn1 = el('[data-bs-target="#childContent1"]')
-      const childBtn2 = el('[data-bs-target="#childContent2"]')
-
-      const parentCollapseEl = el('#parentContent')
-      const childCollapseEl1 = el('#childContent1')
-      const childCollapseEl2 = el('#childContent2')
+    it('should be able to handle toggling of other children siblings', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="parentGroup" class="accordion">',
+          '  <div id="parentHeader" class="accordion-header">',
+          '    <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
+          '  </div>',
+          '  <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
+          '    <div class="accordion-body">',
+          '      <div id="childGroup" class="accordion">',
+          '        <div class="accordion-item">',
+          '          <div id="childHeader1" class="accordion-header">',
+          '            <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
+          '          </div>',
+          '          <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
+          '            <div>content</div>',
+          '          </div>',
+          '        </div>',
+          '        <div class="accordion-item">',
+          '          <div id="childHeader2" class="accordion-header">',
+          '            <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
+          '          </div>',
+          '          <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
+          '            <div>content</div>',
+          '          </div>',
+          '        </div>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const el = selector => fixtureEl.querySelector(selector)
+
+        const parentBtn = el('[data-bs-target="#parentContent"]')
+        const childBtn1 = el('[data-bs-target="#childContent1"]')
+        const childBtn2 = el('[data-bs-target="#childContent2"]')
+
+        const parentCollapseEl = el('#parentContent')
+        const childCollapseEl1 = el('#childContent1')
+        const childCollapseEl2 = el('#childContent2')
+
+        parentCollapseEl.addEventListener('shown.bs.collapse', () => {
+          expect(parentCollapseEl).toHaveClass('show')
+          childBtn1.click()
+        })
+        childCollapseEl1.addEventListener('shown.bs.collapse', () => {
+          expect(childCollapseEl1).toHaveClass('show')
+          childBtn2.click()
+        })
+        childCollapseEl2.addEventListener('shown.bs.collapse', () => {
+          expect(childCollapseEl2).toHaveClass('show')
+          expect(childCollapseEl1).not.toHaveClass('show')
+          resolve()
+        })
 
-      parentCollapseEl.addEventListener('shown.bs.collapse', () => {
-        expect(parentCollapseEl).toHaveClass('show')
-        childBtn1.click()
+        parentBtn.click()
       })
-      childCollapseEl1.addEventListener('shown.bs.collapse', () => {
-        expect(childCollapseEl1).toHaveClass('show')
-        childBtn2.click()
-      })
-      childCollapseEl2.addEventListener('shown.bs.collapse', () => {
-        expect(childCollapseEl2).toHaveClass('show')
-        expect(childCollapseEl1).not.toHaveClass('show')
-        done()
-      })
-
-      parentBtn.click()
     })
 
-    it('should not change tab tabpanels descendants on accordion', done => {
-      fixtureEl.innerHTML = [
-        '<div class="accordion" id="accordionExample">',
-        '  <div class="accordion-item">',
-        '    <h2 class="accordion-header" id="headingOne">',
-        '      <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
-        '        Accordion Item #1',
-        '      </button>',
-        '    </h2>',
-        '    <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
-        '      <div class="accordion-body">',
-        '        <nav>',
-        '          <div class="nav nav-tabs" id="nav-tab" role="tablist">',
-        '            <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
-        '            <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
-        '          </div>',
-        '        </nav>',
-        '        <div class="tab-content" id="nav-tabContent">',
-        '          <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
-        '          <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
-        '        </div>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const el = fixtureEl.querySelector('#collapseOne')
-      const activeTabPane = fixtureEl.querySelector('#nav-home')
-      const collapse = new Collapse(el)
-      let times = 1
+    it('should not change tab tabpanels descendants on accordion', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="accordion" id="accordionExample">',
+          '  <div class="accordion-item">',
+          '    <h2 class="accordion-header" id="headingOne">',
+          '      <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
+          '        Accordion Item #1',
+          '      </button>',
+          '    </h2>',
+          '    <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
+          '      <div class="accordion-body">',
+          '        <nav>',
+          '          <div class="nav nav-tabs" id="nav-tab" role="tablist">',
+          '            <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
+          '            <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
+          '          </div>',
+          '        </nav>',
+          '        <div class="tab-content" id="nav-tabContent">',
+          '          <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
+          '          <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
+          '        </div>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const el = fixtureEl.querySelector('#collapseOne')
+        const activeTabPane = fixtureEl.querySelector('#nav-home')
+        const collapse = new Collapse(el)
+        let times = 1
+
+        el.addEventListener('hidden.bs.collapse', () => {
+          collapse.show()
+        })
 
-      el.addEventListener('hidden.bs.collapse', () => {
-        collapse.show()
-      })
+        el.addEventListener('shown.bs.collapse', () => {
+          expect(activeTabPane).toHaveClass('show')
+          times++
+          if (times === 2) {
+            resolve()
+          }
 
-      el.addEventListener('shown.bs.collapse', () => {
-        expect(activeTabPane).toHaveClass('show')
-        times++
-        if (times === 2) {
-          done()
-        }
+          collapse.hide()
+        })
 
-        collapse.hide()
+        collapse.show()
       })
-
-      collapse.show()
     })
 
-    it('should not fire shown when show is prevented', done => {
-      fixtureEl.innerHTML = '<div class="collapse"></div>'
+    it('should not fire shown when show is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="collapse"></div>'
 
-      const collapseEl = fixtureEl.querySelector('div')
-      const collapse = new Collapse(collapseEl, {
-        toggle: false
-      })
+        const collapseEl = fixtureEl.querySelector('div')
+        const collapse = new Collapse(collapseEl, {
+          toggle: false
+        })
 
-      const expectEnd = () => {
-        setTimeout(() => {
-          expect().nothing()
-          done()
-        }, 10)
-      }
+        const expectEnd = () => {
+          setTimeout(() => {
+            expect().nothing()
+            resolve()
+          }, 10)
+        }
 
-      collapseEl.addEventListener('show.bs.collapse', event => {
-        event.preventDefault()
-        expectEnd()
-      })
+        collapseEl.addEventListener('show.bs.collapse', event => {
+          event.preventDefault()
+          expectEnd()
+        })
 
-      collapseEl.addEventListener('shown.bs.collapse', () => {
-        throw new Error('should not fire shown event')
-      })
+        collapseEl.addEventListener('shown.bs.collapse', () => {
+          throw new Error('should not fire shown event')
+        })
 
-      collapse.show()
+        collapse.show()
+      })
     })
   })
 
@@ -432,48 +446,52 @@ describe('Collapse', () => {
       expect(EventHandler.trigger).not.toHaveBeenCalled()
     })
 
-    it('should hide a collapsed element', done => {
-      fixtureEl.innerHTML = '<div class="collapse show"></div>'
+    it('should hide a collapsed element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="collapse show"></div>'
 
-      const collapseEl = fixtureEl.querySelector('div')
-      const collapse = new Collapse(collapseEl, {
-        toggle: false
-      })
+        const collapseEl = fixtureEl.querySelector('div')
+        const collapse = new Collapse(collapseEl, {
+          toggle: false
+        })
 
-      collapseEl.addEventListener('hidden.bs.collapse', () => {
-        expect(collapseEl).not.toHaveClass('show')
-        expect(collapseEl.style.height).toEqual('')
-        done()
-      })
+        collapseEl.addEventListener('hidden.bs.collapse', () => {
+          expect(collapseEl).not.toHaveClass('show')
+          expect(collapseEl.style.height).toEqual('')
+          resolve()
+        })
 
-      collapse.hide()
+        collapse.hide()
+      })
     })
 
-    it('should not fire hidden when hide is prevented', done => {
-      fixtureEl.innerHTML = '<div class="collapse show"></div>'
+    it('should not fire hidden when hide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="collapse show"></div>'
 
-      const collapseEl = fixtureEl.querySelector('div')
-      const collapse = new Collapse(collapseEl, {
-        toggle: false
-      })
+        const collapseEl = fixtureEl.querySelector('div')
+        const collapse = new Collapse(collapseEl, {
+          toggle: false
+        })
 
-      const expectEnd = () => {
-        setTimeout(() => {
-          expect().nothing()
-          done()
-        }, 10)
-      }
+        const expectEnd = () => {
+          setTimeout(() => {
+            expect().nothing()
+            resolve()
+          }, 10)
+        }
 
-      collapseEl.addEventListener('hide.bs.collapse', event => {
-        event.preventDefault()
-        expectEnd()
-      })
+        collapseEl.addEventListener('hide.bs.collapse', event => {
+          event.preventDefault()
+          expectEnd()
+        })
 
-      collapseEl.addEventListener('hidden.bs.collapse', () => {
-        throw new Error('should not fire hidden event')
-      })
+        collapseEl.addEventListener('hidden.bs.collapse', () => {
+          throw new Error('should not fire hidden event')
+        })
 
-      collapse.hide()
+        collapse.hide()
+      })
     })
   })
 
@@ -495,411 +513,433 @@ describe('Collapse', () => {
   })
 
   describe('data-api', () => {
-    it('should prevent url change if click on nested elements', done => {
-      fixtureEl.innerHTML = [
-        '<a role="button" data-bs-toggle="collapse" class="collapsed" href="#collapse">',
-        '  <span id="nested"></span>',
-        '</a>',
-        '<div id="collapse" class="collapse"></div>'
-      ].join('')
-
-      const triggerEl = fixtureEl.querySelector('a')
-      const nestedTriggerEl = fixtureEl.querySelector('#nested')
-
-      spyOn(Event.prototype, 'preventDefault').and.callThrough()
+    it('should prevent url change if click on nested elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a role="button" data-bs-toggle="collapse" class="collapsed" href="#collapse">',
+          '  <span id="nested"></span>',
+          '</a>',
+          '<div id="collapse" class="collapse"></div>'
+        ].join('')
+
+        const triggerEl = fixtureEl.querySelector('a')
+        const nestedTriggerEl = fixtureEl.querySelector('#nested')
+
+        spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+        triggerEl.addEventListener('click', event => {
+          expect(event.target.isEqualNode(nestedTriggerEl)).toBeTrue()
+          expect(event.delegateTarget.isEqualNode(triggerEl)).toBeTrue()
+          expect(Event.prototype.preventDefault).toHaveBeenCalled()
+          resolve()
+        })
 
-      triggerEl.addEventListener('click', event => {
-        expect(event.target.isEqualNode(nestedTriggerEl)).toBeTrue()
-        expect(event.delegateTarget.isEqualNode(triggerEl)).toBeTrue()
-        expect(Event.prototype.preventDefault).toHaveBeenCalled()
-        done()
+        nestedTriggerEl.click()
       })
-
-      nestedTriggerEl.click()
     })
 
-    it('should show multiple collapsed elements', done => {
-      fixtureEl.innerHTML = [
-        '<a role="button" data-bs-toggle="collapse" class="collapsed" href=".multi"></a>',
-        '<div id="collapse1" class="collapse multi"></div>',
-        '<div id="collapse2" class="collapse multi"></div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('a')
-      const collapse1 = fixtureEl.querySelector('#collapse1')
-      const collapse2 = fixtureEl.querySelector('#collapse2')
+    it('should show multiple collapsed elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a role="button" data-bs-toggle="collapse" class="collapsed" href=".multi"></a>',
+          '<div id="collapse1" class="collapse multi"></div>',
+          '<div id="collapse2" class="collapse multi"></div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('a')
+        const collapse1 = fixtureEl.querySelector('#collapse1')
+        const collapse2 = fixtureEl.querySelector('#collapse2')
+
+        collapse2.addEventListener('shown.bs.collapse', () => {
+          expect(trigger.getAttribute('aria-expanded')).toEqual('true')
+          expect(trigger).not.toHaveClass('collapsed')
+          expect(collapse1).toHaveClass('show')
+          expect(collapse1).toHaveClass('show')
+          resolve()
+        })
 
-      collapse2.addEventListener('shown.bs.collapse', () => {
-        expect(trigger.getAttribute('aria-expanded')).toEqual('true')
-        expect(trigger).not.toHaveClass('collapsed')
-        expect(collapse1).toHaveClass('show')
-        expect(collapse1).toHaveClass('show')
-        done()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should hide multiple collapsed elements', done => {
-      fixtureEl.innerHTML = [
-        '<a role="button" data-bs-toggle="collapse" href=".multi"></a>',
-        '<div id="collapse1" class="collapse multi show"></div>',
-        '<div id="collapse2" class="collapse multi show"></div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('a')
-      const collapse1 = fixtureEl.querySelector('#collapse1')
-      const collapse2 = fixtureEl.querySelector('#collapse2')
+    it('should hide multiple collapsed elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a role="button" data-bs-toggle="collapse" href=".multi"></a>',
+          '<div id="collapse1" class="collapse multi show"></div>',
+          '<div id="collapse2" class="collapse multi show"></div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('a')
+        const collapse1 = fixtureEl.querySelector('#collapse1')
+        const collapse2 = fixtureEl.querySelector('#collapse2')
+
+        collapse2.addEventListener('hidden.bs.collapse', () => {
+          expect(trigger.getAttribute('aria-expanded')).toEqual('false')
+          expect(trigger).toHaveClass('collapsed')
+          expect(collapse1).not.toHaveClass('show')
+          expect(collapse1).not.toHaveClass('show')
+          resolve()
+        })
 
-      collapse2.addEventListener('hidden.bs.collapse', () => {
-        expect(trigger.getAttribute('aria-expanded')).toEqual('false')
-        expect(trigger).toHaveClass('collapsed')
-        expect(collapse1).not.toHaveClass('show')
-        expect(collapse1).not.toHaveClass('show')
-        done()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should remove "collapsed" class from target when collapse is shown', done => {
-      fixtureEl.innerHTML = [
-        '<a id="link1" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
-        '<a id="link2" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
-        '<div id="test1"></div>'
-      ].join('')
-
-      const link1 = fixtureEl.querySelector('#link1')
-      const link2 = fixtureEl.querySelector('#link2')
-      const collapseTest1 = fixtureEl.querySelector('#test1')
+    it('should remove "collapsed" class from target when collapse is shown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a id="link1" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
+          '<a id="link2" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
+          '<div id="test1"></div>'
+        ].join('')
+
+        const link1 = fixtureEl.querySelector('#link1')
+        const link2 = fixtureEl.querySelector('#link2')
+        const collapseTest1 = fixtureEl.querySelector('#test1')
+
+        collapseTest1.addEventListener('shown.bs.collapse', () => {
+          expect(link1.getAttribute('aria-expanded')).toEqual('true')
+          expect(link2.getAttribute('aria-expanded')).toEqual('true')
+          expect(link1).not.toHaveClass('collapsed')
+          expect(link2).not.toHaveClass('collapsed')
+          resolve()
+        })
 
-      collapseTest1.addEventListener('shown.bs.collapse', () => {
-        expect(link1.getAttribute('aria-expanded')).toEqual('true')
-        expect(link2.getAttribute('aria-expanded')).toEqual('true')
-        expect(link1).not.toHaveClass('collapsed')
-        expect(link2).not.toHaveClass('collapsed')
-        done()
+        link1.click()
       })
-
-      link1.click()
     })
 
-    it('should add "collapsed" class to target when collapse is hidden', done => {
-      fixtureEl.innerHTML = [
-        '<a id="link1" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
-        '<a id="link2" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
-        '<div id="test1" class="show"></div>'
-      ].join('')
-
-      const link1 = fixtureEl.querySelector('#link1')
-      const link2 = fixtureEl.querySelector('#link2')
-      const collapseTest1 = fixtureEl.querySelector('#test1')
+    it('should add "collapsed" class to target when collapse is hidden', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a id="link1" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
+          '<a id="link2" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
+          '<div id="test1" class="show"></div>'
+        ].join('')
+
+        const link1 = fixtureEl.querySelector('#link1')
+        const link2 = fixtureEl.querySelector('#link2')
+        const collapseTest1 = fixtureEl.querySelector('#test1')
+
+        collapseTest1.addEventListener('hidden.bs.collapse', () => {
+          expect(link1.getAttribute('aria-expanded')).toEqual('false')
+          expect(link2.getAttribute('aria-expanded')).toEqual('false')
+          expect(link1).toHaveClass('collapsed')
+          expect(link2).toHaveClass('collapsed')
+          resolve()
+        })
 
-      collapseTest1.addEventListener('hidden.bs.collapse', () => {
-        expect(link1.getAttribute('aria-expanded')).toEqual('false')
-        expect(link2.getAttribute('aria-expanded')).toEqual('false')
-        expect(link1).toHaveClass('collapsed')
-        expect(link2).toHaveClass('collapsed')
-        done()
+        link1.click()
       })
-
-      link1.click()
     })
 
-    it('should allow accordion to use children other than card', done => {
-      fixtureEl.innerHTML = [
-        '<div id="accordion">',
-        '  <div class="item">',
-        '    <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
-        '    <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
-        '  </div>',
-        '  <div class="item">',
-        '    <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
-        '    <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('#linkTrigger')
-      const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
-      const collapseOne = fixtureEl.querySelector('#collapseOne')
-      const collapseTwo = fixtureEl.querySelector('#collapseTwo')
-
-      collapseOne.addEventListener('shown.bs.collapse', () => {
-        expect(collapseOne).toHaveClass('show')
-        expect(collapseTwo).not.toHaveClass('show')
+    it('should allow accordion to use children other than card', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="accordion">',
+          '  <div class="item">',
+          '    <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+          '    <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
+          '  </div>',
+          '  <div class="item">',
+          '    <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+          '    <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('#linkTrigger')
+        const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+        const collapseOne = fixtureEl.querySelector('#collapseOne')
+        const collapseTwo = fixtureEl.querySelector('#collapseTwo')
+
+        collapseOne.addEventListener('shown.bs.collapse', () => {
+          expect(collapseOne).toHaveClass('show')
+          expect(collapseTwo).not.toHaveClass('show')
+
+          collapseTwo.addEventListener('shown.bs.collapse', () => {
+            expect(collapseOne).not.toHaveClass('show')
+            expect(collapseTwo).toHaveClass('show')
+            resolve()
+          })
 
-        collapseTwo.addEventListener('shown.bs.collapse', () => {
-          expect(collapseOne).not.toHaveClass('show')
-          expect(collapseTwo).toHaveClass('show')
-          done()
+          triggerTwo.click()
         })
 
-        triggerTwo.click()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should not prevent event for input', done => {
-      fixtureEl.innerHTML = [
-        '<input type="checkbox" data-bs-toggle="collapse" data-bs-target="#collapsediv1">',
-        '<div id="collapsediv1"></div>'
-      ].join('')
+    it('should not prevent event for input', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<input type="checkbox" data-bs-toggle="collapse" data-bs-target="#collapsediv1">',
+          '<div id="collapsediv1"></div>'
+        ].join('')
 
-      const target = fixtureEl.querySelector('input')
-      const collapseEl = fixtureEl.querySelector('#collapsediv1')
+        const target = fixtureEl.querySelector('input')
+        const collapseEl = fixtureEl.querySelector('#collapsediv1')
 
-      collapseEl.addEventListener('shown.bs.collapse', () => {
-        expect(collapseEl).toHaveClass('show')
-        expect(target.checked).toBeTrue()
-        done()
-      })
+        collapseEl.addEventListener('shown.bs.collapse', () => {
+          expect(collapseEl).toHaveClass('show')
+          expect(target.checked).toBeTrue()
+          resolve()
+        })
 
-      target.click()
+        target.click()
+      })
     })
 
-    it('should allow accordion to contain nested elements', done => {
-      fixtureEl.innerHTML = [
-        '<div id="accordion">',
-        '  <div class="row">',
-        '    <div class="col-lg-6">',
-        '      <div class="item">',
-        '        <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
-        '        <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
-        '      </div>',
-        '    </div>',
-        '    <div class="col-lg-6">',
-        '      <div class="item">',
-        '        <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
-        '        <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const triggerEl = fixtureEl.querySelector('#linkTrigger')
-      const triggerTwoEl = fixtureEl.querySelector('#linkTriggerTwo')
-      const collapseOneEl = fixtureEl.querySelector('#collapseOne')
-      const collapseTwoEl = fixtureEl.querySelector('#collapseTwo')
-
-      collapseOneEl.addEventListener('shown.bs.collapse', () => {
-        expect(collapseOneEl).toHaveClass('show')
-        expect(triggerEl).not.toHaveClass('collapsed')
-        expect(triggerEl.getAttribute('aria-expanded')).toEqual('true')
-
-        expect(collapseTwoEl).not.toHaveClass('show')
-        expect(triggerTwoEl).toHaveClass('collapsed')
-        expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('false')
-
-        collapseTwoEl.addEventListener('shown.bs.collapse', () => {
-          expect(collapseOneEl).not.toHaveClass('show')
-          expect(triggerEl).toHaveClass('collapsed')
-          expect(triggerEl.getAttribute('aria-expanded')).toEqual('false')
+    it('should allow accordion to contain nested elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="accordion">',
+          '  <div class="row">',
+          '    <div class="col-lg-6">',
+          '      <div class="item">',
+          '        <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+          '        <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
+          '      </div>',
+          '    </div>',
+          '    <div class="col-lg-6">',
+          '      <div class="item">',
+          '        <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+          '        <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const triggerEl = fixtureEl.querySelector('#linkTrigger')
+        const triggerTwoEl = fixtureEl.querySelector('#linkTriggerTwo')
+        const collapseOneEl = fixtureEl.querySelector('#collapseOne')
+        const collapseTwoEl = fixtureEl.querySelector('#collapseTwo')
+
+        collapseOneEl.addEventListener('shown.bs.collapse', () => {
+          expect(collapseOneEl).toHaveClass('show')
+          expect(triggerEl).not.toHaveClass('collapsed')
+          expect(triggerEl.getAttribute('aria-expanded')).toEqual('true')
+
+          expect(collapseTwoEl).not.toHaveClass('show')
+          expect(triggerTwoEl).toHaveClass('collapsed')
+          expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('false')
+
+          collapseTwoEl.addEventListener('shown.bs.collapse', () => {
+            expect(collapseOneEl).not.toHaveClass('show')
+            expect(triggerEl).toHaveClass('collapsed')
+            expect(triggerEl.getAttribute('aria-expanded')).toEqual('false')
+
+            expect(collapseTwoEl).toHaveClass('show')
+            expect(triggerTwoEl).not.toHaveClass('collapsed')
+            expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('true')
+            resolve()
+          })
 
-          expect(collapseTwoEl).toHaveClass('show')
-          expect(triggerTwoEl).not.toHaveClass('collapsed')
-          expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('true')
-          done()
+          triggerTwoEl.click()
         })
 
-        triggerTwoEl.click()
+        triggerEl.click()
       })
-
-      triggerEl.click()
     })
 
-    it('should allow accordion to target multiple elements', done => {
-      fixtureEl.innerHTML = [
-        '<div id="accordion">',
-        '  <a id="linkTriggerOne" data-bs-toggle="collapse" data-bs-target=".collapseOne" href="#" aria-expanded="false" aria-controls="collapseOne"></a>',
-        '  <a id="linkTriggerTwo" data-bs-toggle="collapse" data-bs-target=".collapseTwo" href="#" aria-expanded="false" aria-controls="collapseTwo"></a>',
-        '  <div id="collapseOneOne" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
-        '  <div id="collapseOneTwo" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
-        '  <div id="collapseTwoOne" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
-        '  <div id="collapseTwoTwo" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
-        '</div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('#linkTriggerOne')
-      const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
-      const collapseOneOne = fixtureEl.querySelector('#collapseOneOne')
-      const collapseOneTwo = fixtureEl.querySelector('#collapseOneTwo')
-      const collapseTwoOne = fixtureEl.querySelector('#collapseTwoOne')
-      const collapseTwoTwo = fixtureEl.querySelector('#collapseTwoTwo')
-      const collapsedElements = {
-        one: false,
-        two: false
-      }
-
-      function firstTest() {
-        expect(collapseOneOne).toHaveClass('show')
-        expect(collapseOneTwo).toHaveClass('show')
-
-        expect(collapseTwoOne).not.toHaveClass('show')
-        expect(collapseTwoTwo).not.toHaveClass('show')
-
-        triggerTwo.click()
-      }
-
-      function secondTest() {
-        expect(collapseOneOne).not.toHaveClass('show')
-        expect(collapseOneTwo).not.toHaveClass('show')
-
-        expect(collapseTwoOne).toHaveClass('show')
-        expect(collapseTwoTwo).toHaveClass('show')
-        done()
-      }
-
-      collapseOneOne.addEventListener('shown.bs.collapse', () => {
-        if (collapsedElements.one) {
-          firstTest()
-        } else {
-          collapsedElements.one = true
+    it('should allow accordion to target multiple elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="accordion">',
+          '  <a id="linkTriggerOne" data-bs-toggle="collapse" data-bs-target=".collapseOne" href="#" aria-expanded="false" aria-controls="collapseOne"></a>',
+          '  <a id="linkTriggerTwo" data-bs-toggle="collapse" data-bs-target=".collapseTwo" href="#" aria-expanded="false" aria-controls="collapseTwo"></a>',
+          '  <div id="collapseOneOne" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
+          '  <div id="collapseOneTwo" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
+          '  <div id="collapseTwoOne" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
+          '  <div id="collapseTwoTwo" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
+          '</div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('#linkTriggerOne')
+        const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+        const collapseOneOne = fixtureEl.querySelector('#collapseOneOne')
+        const collapseOneTwo = fixtureEl.querySelector('#collapseOneTwo')
+        const collapseTwoOne = fixtureEl.querySelector('#collapseTwoOne')
+        const collapseTwoTwo = fixtureEl.querySelector('#collapseTwoTwo')
+        const collapsedElements = {
+          one: false,
+          two: false
         }
-      })
 
-      collapseOneTwo.addEventListener('shown.bs.collapse', () => {
-        if (collapsedElements.one) {
-          firstTest()
-        } else {
-          collapsedElements.one = true
-        }
-      })
+        function firstTest() {
+          expect(collapseOneOne).toHaveClass('show')
+          expect(collapseOneTwo).toHaveClass('show')
 
-      collapseTwoOne.addEventListener('shown.bs.collapse', () => {
-        if (collapsedElements.two) {
-          secondTest()
-        } else {
-          collapsedElements.two = true
-        }
-      })
+          expect(collapseTwoOne).not.toHaveClass('show')
+          expect(collapseTwoTwo).not.toHaveClass('show')
 
-      collapseTwoTwo.addEventListener('shown.bs.collapse', () => {
-        if (collapsedElements.two) {
-          secondTest()
-        } else {
-          collapsedElements.two = true
+          triggerTwo.click()
         }
-      })
 
-      trigger.click()
-    })
+        function secondTest() {
+          expect(collapseOneOne).not.toHaveClass('show')
+          expect(collapseOneTwo).not.toHaveClass('show')
 
-    it('should collapse accordion children but not nested accordion children', done => {
-      fixtureEl.innerHTML = [
-        '<div id="accordion">',
-        '  <div class="item">',
-        '    <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
-        '    <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">',
-        '      <div id="nestedAccordion">',
-        '        <div class="item">',
-        '          <a id="nestedLinkTrigger" data-bs-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>',
-        '          <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel" aria-labelledby="headingThree"></div>',
-        '        </div>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '  <div class="item">',
-        '    <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
-        '    <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel" aria-labelledby="headingTwo"></div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+          expect(collapseTwoOne).toHaveClass('show')
+          expect(collapseTwoTwo).toHaveClass('show')
+          resolve()
+        }
 
-      const trigger = fixtureEl.querySelector('#linkTrigger')
-      const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
-      const nestedTrigger = fixtureEl.querySelector('#nestedLinkTrigger')
-      const collapseOne = fixtureEl.querySelector('#collapseOne')
-      const collapseTwo = fixtureEl.querySelector('#collapseTwo')
-      const nestedCollapseOne = fixtureEl.querySelector('#nestedCollapseOne')
-
-      function handlerCollapseOne() {
-        expect(collapseOne).toHaveClass('show')
-        expect(collapseTwo).not.toHaveClass('show')
-        expect(nestedCollapseOne).not.toHaveClass('show')
-
-        nestedCollapseOne.addEventListener('shown.bs.collapse', handlerNestedCollapseOne)
-        nestedTrigger.click()
-        collapseOne.removeEventListener('shown.bs.collapse', handlerCollapseOne)
-      }
+        collapseOneOne.addEventListener('shown.bs.collapse', () => {
+          if (collapsedElements.one) {
+            firstTest()
+          } else {
+            collapsedElements.one = true
+          }
+        })
 
-      function handlerNestedCollapseOne() {
-        expect(collapseOne).toHaveClass('show')
-        expect(collapseTwo).not.toHaveClass('show')
-        expect(nestedCollapseOne).toHaveClass('show')
+        collapseOneTwo.addEventListener('shown.bs.collapse', () => {
+          if (collapsedElements.one) {
+            firstTest()
+          } else {
+            collapsedElements.one = true
+          }
+        })
 
-        collapseTwo.addEventListener('shown.bs.collapse', () => {
-          expect(collapseOne).not.toHaveClass('show')
-          expect(collapseTwo).toHaveClass('show')
-          expect(nestedCollapseOne).toHaveClass('show')
-          done()
+        collapseTwoOne.addEventListener('shown.bs.collapse', () => {
+          if (collapsedElements.two) {
+            secondTest()
+          } else {
+            collapsedElements.two = true
+          }
         })
 
-        triggerTwo.click()
-        nestedCollapseOne.removeEventListener('shown.bs.collapse', handlerNestedCollapseOne)
-      }
+        collapseTwoTwo.addEventListener('shown.bs.collapse', () => {
+          if (collapsedElements.two) {
+            secondTest()
+          } else {
+            collapsedElements.two = true
+          }
+        })
 
-      collapseOne.addEventListener('shown.bs.collapse', handlerCollapseOne)
-      trigger.click()
+        trigger.click()
+      })
     })
 
-    it('should add "collapsed" class and set aria-expanded to triggers only when all the targeted collapse are hidden', done => {
-      fixtureEl.innerHTML = [
-        '<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
-        '<a id="trigger2" role="button" data-bs-toggle="collapse" href="#test2"></a>',
-        '<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
-        '<div id="test1" class="multi"></div>',
-        '<div id="test2" class="multi"></div>'
-      ].join('')
+    it('should collapse accordion children but not nested accordion children', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="accordion">',
+          '  <div class="item">',
+          '    <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+          '    <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">',
+          '      <div id="nestedAccordion">',
+          '        <div class="item">',
+          '          <a id="nestedLinkTrigger" data-bs-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>',
+          '          <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel" aria-labelledby="headingThree"></div>',
+          '        </div>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '  <div class="item">',
+          '    <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+          '    <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel" aria-labelledby="headingTwo"></div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('#linkTrigger')
+        const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+        const nestedTrigger = fixtureEl.querySelector('#nestedLinkTrigger')
+        const collapseOne = fixtureEl.querySelector('#collapseOne')
+        const collapseTwo = fixtureEl.querySelector('#collapseTwo')
+        const nestedCollapseOne = fixtureEl.querySelector('#nestedCollapseOne')
+
+        function handlerCollapseOne() {
+          expect(collapseOne).toHaveClass('show')
+          expect(collapseTwo).not.toHaveClass('show')
+          expect(nestedCollapseOne).not.toHaveClass('show')
+
+          nestedCollapseOne.addEventListener('shown.bs.collapse', handlerNestedCollapseOne)
+          nestedTrigger.click()
+          collapseOne.removeEventListener('shown.bs.collapse', handlerCollapseOne)
+        }
 
-      const trigger1 = fixtureEl.querySelector('#trigger1')
-      const trigger2 = fixtureEl.querySelector('#trigger2')
-      const trigger3 = fixtureEl.querySelector('#trigger3')
-      const target1 = fixtureEl.querySelector('#test1')
-      const target2 = fixtureEl.querySelector('#test2')
+        function handlerNestedCollapseOne() {
+          expect(collapseOne).toHaveClass('show')
+          expect(collapseTwo).not.toHaveClass('show')
+          expect(nestedCollapseOne).toHaveClass('show')
 
-      const target2Shown = () => {
-        expect(trigger1).not.toHaveClass('collapsed')
-        expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
+          collapseTwo.addEventListener('shown.bs.collapse', () => {
+            expect(collapseOne).not.toHaveClass('show')
+            expect(collapseTwo).toHaveClass('show')
+            expect(nestedCollapseOne).toHaveClass('show')
+            resolve()
+          })
 
-        expect(trigger2).not.toHaveClass('collapsed')
-        expect(trigger2.getAttribute('aria-expanded')).toEqual('true')
+          triggerTwo.click()
+          nestedCollapseOne.removeEventListener('shown.bs.collapse', handlerNestedCollapseOne)
+        }
 
-        expect(trigger3).not.toHaveClass('collapsed')
-        expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
+        collapseOne.addEventListener('shown.bs.collapse', handlerCollapseOne)
+        trigger.click()
+      })
+    })
 
-        target2.addEventListener('hidden.bs.collapse', () => {
+    it('should add "collapsed" class and set aria-expanded to triggers only when all the targeted collapse are hidden', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
+          '<a id="trigger2" role="button" data-bs-toggle="collapse" href="#test2"></a>',
+          '<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
+          '<div id="test1" class="multi"></div>',
+          '<div id="test2" class="multi"></div>'
+        ].join('')
+
+        const trigger1 = fixtureEl.querySelector('#trigger1')
+        const trigger2 = fixtureEl.querySelector('#trigger2')
+        const trigger3 = fixtureEl.querySelector('#trigger3')
+        const target1 = fixtureEl.querySelector('#test1')
+        const target2 = fixtureEl.querySelector('#test2')
+
+        const target2Shown = () => {
           expect(trigger1).not.toHaveClass('collapsed')
           expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
 
-          expect(trigger2).toHaveClass('collapsed')
-          expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
+          expect(trigger2).not.toHaveClass('collapsed')
+          expect(trigger2.getAttribute('aria-expanded')).toEqual('true')
 
           expect(trigger3).not.toHaveClass('collapsed')
           expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
 
-          target1.addEventListener('hidden.bs.collapse', () => {
-            expect(trigger1).toHaveClass('collapsed')
-            expect(trigger1.getAttribute('aria-expanded')).toEqual('false')
+          target2.addEventListener('hidden.bs.collapse', () => {
+            expect(trigger1).not.toHaveClass('collapsed')
+            expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
 
             expect(trigger2).toHaveClass('collapsed')
             expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
 
-            expect(trigger3).toHaveClass('collapsed')
-            expect(trigger3.getAttribute('aria-expanded')).toEqual('false')
-            done()
-          })
+            expect(trigger3).not.toHaveClass('collapsed')
+            expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
 
-          trigger1.click()
-        })
+            target1.addEventListener('hidden.bs.collapse', () => {
+              expect(trigger1).toHaveClass('collapsed')
+              expect(trigger1.getAttribute('aria-expanded')).toEqual('false')
 
-        trigger2.click()
-      }
+              expect(trigger2).toHaveClass('collapsed')
+              expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
+
+              expect(trigger3).toHaveClass('collapsed')
+              expect(trigger3.getAttribute('aria-expanded')).toEqual('false')
+              resolve()
+            })
+
+            trigger1.click()
+          })
 
-      target2.addEventListener('shown.bs.collapse', target2Shown)
-      trigger3.click()
+          trigger2.click()
+        }
+
+        target2.addEventListener('shown.bs.collapse', target2Shown)
+        trigger3.click()
+      })
     })
   })
 
index 601af7409ef90240feed3fbd1d69cf999e05ce08..e5887ce70892468c34ca83e34d0caf8c313dcbef 100644 (file)
@@ -1,5 +1,6 @@
 import EventHandler from '../../../src/dom/event-handler'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import { clearFixture, getFixture } from '../../helpers/fixture'
+import { noop } from '../../../src/util'
 
 describe('EventHandler', () => {
   let fixtureEl
@@ -18,176 +19,190 @@ describe('EventHandler', () => {
 
       const div = fixtureEl.querySelector('div')
 
-      EventHandler.on(div, null, () => {})
-      EventHandler.on(null, 'click', () => {})
+      EventHandler.on(div, null, noop)
+      EventHandler.on(null, 'click', noop)
 
       expect().nothing()
     })
 
-    it('should add event listener', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should add event listener', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      const div = fixtureEl.querySelector('div')
+        const div = fixtureEl.querySelector('div')
 
-      EventHandler.on(div, 'click', () => {
-        expect().nothing()
-        done()
-      })
+        EventHandler.on(div, 'click', () => {
+          expect().nothing()
+          resolve()
+        })
 
-      div.click()
+        div.click()
+      })
     })
 
-    it('should add namespaced event listener', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should add namespaced event listener', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      const div = fixtureEl.querySelector('div')
+        const div = fixtureEl.querySelector('div')
 
-      EventHandler.on(div, 'bs.namespace', () => {
-        expect().nothing()
-        done()
-      })
+        EventHandler.on(div, 'bs.namespace', () => {
+          expect().nothing()
+          resolve()
+        })
 
-      EventHandler.trigger(div, 'bs.namespace')
+        EventHandler.trigger(div, 'bs.namespace')
+      })
     })
 
-    it('should add native namespaced event listener', done => {
-      fixtureEl.innerHTML = '<div></div>'
-
-      const div = fixtureEl.querySelector('div')
+    it('should add native namespaced event listener', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      EventHandler.on(div, 'click.namespace', () => {
-        expect().nothing()
-        done()
-      })
+        const div = fixtureEl.querySelector('div')
 
-      EventHandler.trigger(div, 'click')
-    })
+        EventHandler.on(div, 'click.namespace', () => {
+          expect().nothing()
+          resolve()
+        })
 
-    it('should handle event delegation', done => {
-      EventHandler.on(document, 'click', '.test', () => {
-        expect().nothing()
-        done()
+        EventHandler.trigger(div, 'click')
       })
+    })
 
-      fixtureEl.innerHTML = '<div class="test"></div>'
+    it('should handle event delegation', () => {
+      return new Promise(resolve => {
+        EventHandler.on(document, 'click', '.test', () => {
+          expect().nothing()
+          resolve()
+        })
 
-      const div = fixtureEl.querySelector('div')
+        fixtureEl.innerHTML = '<div class="test"></div>'
 
-      div.click()
-    })
+        const div = fixtureEl.querySelector('div')
 
-    it('should handle mouseenter/mouseleave like the native counterpart', done => {
-      fixtureEl.innerHTML = [
-        '<div class="outer">',
-        '<div class="inner">',
-        '<div class="nested">',
-        '<div class="deep"></div>',
-        '</div>',
-        '</div>',
-        '<div class="sibling"></div>',
-        '</div>'
-      ].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)
-        done()
+        div.click()
       })
+    })
 
-      const moveMouse = (from, to) => {
-        from.dispatchEvent(new MouseEvent('mouseout', {
-          bubbles: true,
-          relatedTarget: to
-        }))
-
-        to.dispatchEvent(new MouseEvent('mouseover', {
-          bubbles: true,
-          relatedTarget: from
-        }))
-      }
+    it('should handle mouseenter/mouseleave like the native counterpart', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="outer">',
+          '<div class="inner">',
+          '<div class="nested">',
+          '<div class="deep"></div>',
+          '</div>',
+          '</div>',
+          '<div class="sibling"></div>',
+          '</div>'
+        ].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)
+        // from outer to deep and back to outer (nested)
         moveMouse(outer, inner)
-        moveMouse(inner, sibling)
-      }, 20)
+        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', done => {
-      fixtureEl.innerHTML = '<div></div>'
-
-      let called = 0
-      const div = fixtureEl.querySelector('div')
-      const obj = {
-        oneListener() {
-          called++
+    it('should call listener just once', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+
+        let called = 0
+        const div = fixtureEl.querySelector('div')
+        const obj = {
+          oneListener() {
+            called++
+          }
         }
-      }
 
-      EventHandler.one(div, 'bootstrap', obj.oneListener)
+        EventHandler.one(div, 'bootstrap', obj.oneListener)
 
-      EventHandler.trigger(div, 'bootstrap')
-      EventHandler.trigger(div, 'bootstrap')
+        EventHandler.trigger(div, 'bootstrap')
+        EventHandler.trigger(div, 'bootstrap')
 
-      setTimeout(() => {
-        expect(called).toEqual(1)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(called).toEqual(1)
+          resolve()
+        }, 20)
+      })
     })
 
-    it('should call delegated listener just once', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should call delegated listener just once', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      let called = 0
-      const div = fixtureEl.querySelector('div')
-      const obj = {
-        oneListener() {
-          called++
+        let called = 0
+        const div = fixtureEl.querySelector('div')
+        const obj = {
+          oneListener() {
+            called++
+          }
         }
-      }
 
-      EventHandler.one(fixtureEl, 'bootstrap', 'div', obj.oneListener)
+        EventHandler.one(fixtureEl, 'bootstrap', 'div', obj.oneListener)
 
-      EventHandler.trigger(div, 'bootstrap')
-      EventHandler.trigger(div, 'bootstrap')
+        EventHandler.trigger(div, 'bootstrap')
+        EventHandler.trigger(div, 'bootstrap')
 
-      setTimeout(() => {
-        expect(called).toEqual(1)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(called).toEqual(1)
+          resolve()
+        }, 20)
+      })
     })
   })
 
@@ -196,171 +211,185 @@ describe('EventHandler', () => {
       fixtureEl.innerHTML = '<div></div>'
       const div = fixtureEl.querySelector('div')
 
-      EventHandler.off(div, null, () => {})
-      EventHandler.off(null, 'click', () => {})
+      EventHandler.off(div, null, noop)
+      EventHandler.off(null, 'click', noop)
       expect().nothing()
     })
 
-    it('should remove a listener', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
+    it('should remove a listener', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        const div = fixtureEl.querySelector('div')
 
-      let called = 0
-      const handler = () => {
-        called++
-      }
+        let called = 0
+        const handler = () => {
+          called++
+        }
 
-      EventHandler.on(div, 'foobar', handler)
-      EventHandler.trigger(div, 'foobar')
+        EventHandler.on(div, 'foobar', handler)
+        EventHandler.trigger(div, 'foobar')
 
-      EventHandler.off(div, 'foobar', handler)
-      EventHandler.trigger(div, 'foobar')
+        EventHandler.off(div, 'foobar', handler)
+        EventHandler.trigger(div, 'foobar')
 
-      setTimeout(() => {
-        expect(called).toEqual(1)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(called).toEqual(1)
+          resolve()
+        }, 20)
+      })
     })
 
-    it('should remove all the events', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
+    it('should remove all the events', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        const div = fixtureEl.querySelector('div')
 
-      let called = 0
+        let called = 0
 
-      EventHandler.on(div, 'foobar', () => {
-        called++
-      })
-      EventHandler.on(div, 'foobar', () => {
-        called++
-      })
-      EventHandler.trigger(div, 'foobar')
+        EventHandler.on(div, 'foobar', () => {
+          called++
+        })
+        EventHandler.on(div, 'foobar', () => {
+          called++
+        })
+        EventHandler.trigger(div, 'foobar')
 
-      EventHandler.off(div, 'foobar')
-      EventHandler.trigger(div, 'foobar')
+        EventHandler.off(div, 'foobar')
+        EventHandler.trigger(div, 'foobar')
 
-      setTimeout(() => {
-        expect(called).toEqual(2)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(called).toEqual(2)
+          resolve()
+        }, 20)
+      })
     })
 
-    it('should remove all the namespaced listeners if namespace is passed', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
+    it('should remove all the namespaced listeners if namespace is passed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        const div = fixtureEl.querySelector('div')
 
-      let called = 0
+        let called = 0
 
-      EventHandler.on(div, 'foobar.namespace', () => {
-        called++
-      })
-      EventHandler.on(div, 'foofoo.namespace', () => {
-        called++
+        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)
       })
-      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)
-        done()
-      }, 20)
     })
 
-    it('should remove the namespaced listeners', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
+    it('should remove the namespaced listeners', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        const div = fixtureEl.querySelector('div')
 
-      let calledCallback1 = 0
-      let calledCallback2 = 0
+        let calledCallback1 = 0
+        let calledCallback2 = 0
 
-      EventHandler.on(div, 'foobar.namespace', () => {
-        calledCallback1++
-      })
-      EventHandler.on(div, 'foofoo.namespace', () => {
-        calledCallback2++
-      })
+        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, 'foobar.namespace')
+        EventHandler.off(div, 'foobar.namespace')
+        EventHandler.trigger(div, 'foobar.namespace')
 
-      EventHandler.trigger(div, 'foofoo.namespace')
+        EventHandler.trigger(div, 'foofoo.namespace')
 
-      setTimeout(() => {
-        expect(calledCallback1).toEqual(1)
-        expect(calledCallback2).toEqual(1)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(calledCallback1).toEqual(1)
+          expect(calledCallback2).toEqual(1)
+          resolve()
+        }, 20)
+      })
     })
 
-    it('should remove the all the namespaced listeners for native events', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
+    it('should remove the all the namespaced listeners for native events', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        const div = fixtureEl.querySelector('div')
 
-      let called = 0
+        let called = 0
 
-      EventHandler.on(div, 'click.namespace', () => {
-        called++
-      })
-      EventHandler.on(div, 'click.namespace2', () => {
-        called++
-      })
+        EventHandler.on(div, 'click.namespace', () => {
+          called++
+        })
+        EventHandler.on(div, 'click.namespace2', () => {
+          called++
+        })
 
-      EventHandler.trigger(div, 'click')
-      EventHandler.off(div, 'click')
-      EventHandler.trigger(div, 'click')
+        EventHandler.trigger(div, 'click')
+        EventHandler.off(div, 'click')
+        EventHandler.trigger(div, 'click')
 
-      setTimeout(() => {
-        expect(called).toEqual(2)
-        done()
-      }, 20)
+        setTimeout(() => {
+          expect(called).toEqual(2)
+          resolve()
+        }, 20)
+      })
     })
 
-    it('should remove the specified namespaced listeners for native events', done => {
-      fixtureEl.innerHTML = '<div></div>'
-      const div = fixtureEl.querySelector('div')
-
-      let called1 = 0
-      let called2 = 0
-
-      EventHandler.on(div, 'click.namespace', () => {
-        called1++
+    it('should remove the specified namespaced listeners for native events', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+        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)
       })
-      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)
-        done()
-      }, 20)
     })
 
-    it('should remove a listener registered by .one', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should remove a listener registered by .one', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      const div = fixtureEl.querySelector('div')
-      const handler = () => {
-        throw new Error('called')
-      }
+        const div = fixtureEl.querySelector('div')
+        const handler = () => {
+          throw new Error('called')
+        }
 
-      EventHandler.one(div, 'foobar', handler)
-      EventHandler.off(div, 'foobar', handler)
+        EventHandler.one(div, 'foobar', handler)
+        EventHandler.off(div, 'foobar', handler)
 
-      EventHandler.trigger(div, 'foobar')
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 20)
+        EventHandler.trigger(div, 'foobar')
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 20)
+      })
     })
 
     it('should remove the correct delegated event listener', () => {
index 963ee5989f31bf108b9353444dcaeb9864caa099..95af902ff546b17ed5096ee312b554d4f3ce784e 100644 (file)
@@ -1,5 +1,5 @@
 import Manipulator from '../../../src/dom/manipulator'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import { clearFixture, getFixture } from '../../helpers/fixture'
 
 describe('Manipulator', () => {
   let fixtureEl
@@ -134,42 +134,44 @@ describe('Manipulator', () => {
       })
     })
 
-    it('should not change offset when viewport is scrolled', done => {
-      const top = 500
-      const left = 1000
-      const scrollY = 200
-      const scrollX = 400
+    it('should not change offset when viewport is scrolled', () => {
+      return new Promise(resolve => {
+        const top = 500
+        const left = 1000
+        const scrollY = 200
+        const scrollX = 400
 
-      fixtureEl.innerHTML = `<div style="position:absolute;top:${top}px;left:${left}px"></div>`
+        fixtureEl.innerHTML = `<div style="position:absolute;top:${top}px;left:${left}px"></div>`
 
-      const div = fixtureEl.querySelector('div')
-      const offset = Manipulator.offset(div)
+        const div = fixtureEl.querySelector('div')
+        const offset = Manipulator.offset(div)
 
-      // append an element that forces scrollbars on the window so we can scroll
-      const { defaultView: win, body } = fixtureEl.ownerDocument
-      const forceScrollBars = document.createElement('div')
-      forceScrollBars.style.cssText = 'position:absolute;top:5000px;left:5000px;width:1px;height:1px'
-      body.append(forceScrollBars)
+        // append an element that forces scrollbars on the window so we can scroll
+        const { defaultView: win, body } = fixtureEl.ownerDocument
+        const forceScrollBars = document.createElement('div')
+        forceScrollBars.style.cssText = 'position:absolute;top:5000px;left:5000px;width:1px;height:1px'
+        body.append(forceScrollBars)
 
-      const scrollHandler = () => {
-        expect(window.pageYOffset).toEqual(scrollY)
-        expect(window.pageXOffset).toEqual(scrollX)
+        const scrollHandler = () => {
+          expect(window.pageYOffset).toEqual(scrollY)
+          expect(window.pageXOffset).toEqual(scrollX)
 
-        const newOffset = Manipulator.offset(div)
+          const newOffset = Manipulator.offset(div)
 
-        expect(newOffset).toEqual({
-          top: offset.top,
-          left: offset.left
-        })
+          expect(newOffset).toEqual({
+            top: offset.top,
+            left: offset.left
+          })
 
-        win.removeEventListener('scroll', scrollHandler)
-        forceScrollBars.remove()
-        win.scrollTo(0, 0)
-        done()
-      }
+          win.removeEventListener('scroll', scrollHandler)
+          forceScrollBars.remove()
+          win.scrollTo(0, 0)
+          resolve()
+        }
 
-      win.addEventListener('scroll', scrollHandler)
-      win.scrollTo(scrollX, scrollY)
+        win.addEventListener('scroll', scrollHandler)
+        win.scrollTo(scrollX, scrollY)
+      })
     })
   })
 
index 6ee58fe71a0ac9254b8a3af4731c69b938b78df5..f24b59ed5c93bd026e8b89355cf641d9d5f28db1 100644 (file)
@@ -57,36 +57,38 @@ describe('Dropdown', () => {
       expect(dropdownByElement._element).toEqual(btnDropdown)
     })
 
-    it('should create offset modifier correctly when offset option is a function', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        offset: getOffset,
-        popperConfig: {
-          onFirstUpdate: state => {
-            expect(getOffset).toHaveBeenCalledWith({
-              popper: state.rects.popper,
-              reference: state.rects.reference,
-              placement: state.placement
-            }, btnDropdown)
-            done()
+    it('should create offset modifier correctly when offset option is a function', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown, {
+          offset: getOffset,
+          popperConfig: {
+            onFirstUpdate: state => {
+              expect(getOffset).toHaveBeenCalledWith({
+                popper: state.rects.popper,
+                reference: state.rects.reference,
+                placement: state.placement
+              }, btnDropdown)
+              resolve()
+            }
           }
-        }
-      })
-      const offset = dropdown._getOffset()
+        })
+        const offset = dropdown._getOffset()
 
-      expect(typeof offset).toEqual('function')
+        expect(typeof offset).toEqual('function')
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
 
     it('should create offset modifier correctly when offset option is a string into data attribute', () => {
@@ -151,761 +153,817 @@ describe('Dropdown', () => {
   })
 
   describe('toggle', () => {
-    it('should toggle a dropdown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+    it('should toggle a dropdown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
+
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
+        dropdown.toggle()
       })
-
-      dropdown.toggle()
     })
 
-    it('should destroy old popper references on toggle', done => {
-      fixtureEl.innerHTML = [
-        '<div class="first dropdown">',
-        '  <button class="firstBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>',
-        '<div class="second dropdown">',
-        '  <button class="secondBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should destroy old popper references on toggle', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="first dropdown">',
+          '  <button class="firstBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>',
+          '<div class="second dropdown">',
+          '  <button class="secondBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown1 = fixtureEl.querySelector('.firstBtn')
+        const btnDropdown2 = fixtureEl.querySelector('.secondBtn')
+        const firstDropdownEl = fixtureEl.querySelector('.first')
+        const secondDropdownEl = fixtureEl.querySelector('.second')
+        const dropdown1 = new Dropdown(btnDropdown1)
+
+        firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown1).toHaveClass('show')
+          spyOn(dropdown1._popper, 'destroy')
+          btnDropdown2.click()
+        })
 
-      const btnDropdown1 = fixtureEl.querySelector('.firstBtn')
-      const btnDropdown2 = fixtureEl.querySelector('.secondBtn')
-      const firstDropdownEl = fixtureEl.querySelector('.first')
-      const secondDropdownEl = fixtureEl.querySelector('.second')
-      const dropdown1 = new Dropdown(btnDropdown1)
+        secondDropdownEl.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
+          expect(dropdown1._popper.destroy).toHaveBeenCalled()
+          resolve()
+        }))
 
-      firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown1).toHaveClass('show')
-        spyOn(dropdown1._popper, 'destroy')
-        btnDropdown2.click()
+        dropdown1.toggle()
       })
-
-      secondDropdownEl.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
-        expect(dropdown1._popper.destroy).toHaveBeenCalled()
-        done()
-      }))
-
-      dropdown1.toggle()
     })
 
-    it('should toggle a dropdown and add/remove event listener on mobile', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropdown and add/remove event listener on mobile', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const defaultValueOnTouchStart = document.documentElement.ontouchstart
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const defaultValueOnTouchStart = document.documentElement.ontouchstart
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      document.documentElement.ontouchstart = noop
-      spyOn(EventHandler, 'on')
-      spyOn(EventHandler, 'off')
+        document.documentElement.ontouchstart = noop
+        spyOn(EventHandler, 'on')
+        spyOn(EventHandler, 'off')
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
 
-        dropdown.toggle()
-      })
+          dropdown.toggle()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(btnDropdown).not.toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
-        expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(btnDropdown).not.toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+          expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
 
-        document.documentElement.ontouchstart = defaultValueOnTouchStart
-        done()
-      })
+          document.documentElement.ontouchstart = defaultValueOnTouchStart
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropdown at the right', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu dropdown-menu-end">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropdown at the right', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu dropdown-menu-end">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropup', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropup">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropup', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropup">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropupEl = fixtureEl.querySelector('.dropup')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropupEl = fixtureEl.querySelector('.dropup')
+        const dropdown = new Dropdown(btnDropdown)
 
-      dropupEl.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        dropupEl.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropup at the right', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropup">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu dropdown-menu-end">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropup at the right', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropup">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu dropdown-menu-end">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropupEl = fixtureEl.querySelector('.dropup')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropupEl = fixtureEl.querySelector('.dropup')
+        const dropdown = new Dropdown(btnDropdown)
 
-      dropupEl.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        dropupEl.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropend', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropend">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropend', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropend">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropendEl = fixtureEl.querySelector('.dropend')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropendEl = fixtureEl.querySelector('.dropend')
+        const dropdown = new Dropdown(btnDropdown)
 
-      dropendEl.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        dropendEl.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropstart', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropstart">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropstart', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropstart">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropstartEl = fixtureEl.querySelector('.dropstart')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropstartEl = fixtureEl.querySelector('.dropstart')
+        const dropdown = new Dropdown(btnDropdown)
 
-      dropstartEl.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        dropstartEl.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropdown with parent reference', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropdown with parent reference', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        reference: 'parent'
-      })
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown, {
+          reference: 'parent'
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropdown with a dom node reference', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropdown with a dom node reference', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        reference: fixtureEl
-      })
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown, {
+          reference: fixtureEl
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropdown with a jquery object reference', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should toggle a dropdown with a jquery object reference', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        reference: { 0: fixtureEl, jquery: 'jQuery' }
-      })
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown, {
+          reference: { 0: fixtureEl, jquery: 'jQuery' }
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          resolve()
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should toggle a dropdown with a valid virtual element reference', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle visually-hidden" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const virtualElement = {
-        nodeType: 1,
-        getBoundingClientRect() {
-          return {
-            width: 0,
-            height: 0,
-            top: 0,
-            right: 0,
-            bottom: 0,
-            left: 0
+    it('should toggle a dropdown with a valid virtual element reference', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle visually-hidden" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const virtualElement = {
+          nodeType: 1,
+          getBoundingClientRect() {
+            return {
+              width: 0,
+              height: 0,
+              top: 0,
+              right: 0,
+              bottom: 0,
+              left: 0
+            }
           }
         }
-      }
 
-      expect(() => new Dropdown(btnDropdown, {
-        reference: {}
-      })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
+        expect(() => new Dropdown(btnDropdown, {
+          reference: {}
+        })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
 
-      expect(() => new Dropdown(btnDropdown, {
-        reference: {
-          getBoundingClientRect: 'not-a-function'
-        }
-      })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
-
-      // use onFirstUpdate as Poppers internal update is executed async
-      const dropdown = new Dropdown(btnDropdown, {
-        reference: virtualElement,
-        popperConfig: {
-          onFirstUpdate() {
-            expect(virtualElement.getBoundingClientRect).toHaveBeenCalled()
-            expect(btnDropdown).toHaveClass('show')
-            expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-            done()
+        expect(() => new Dropdown(btnDropdown, {
+          reference: {
+            getBoundingClientRect: 'not-a-function'
           }
-        }
-      })
+        })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
+
+        // use onFirstUpdate as Poppers internal update is executed async
+        const dropdown = new Dropdown(btnDropdown, {
+          reference: virtualElement,
+          popperConfig: {
+            onFirstUpdate() {
+              expect(virtualElement.getBoundingClientRect).toHaveBeenCalled()
+              expect(btnDropdown).toHaveClass('show')
+              expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+              resolve()
+            }
+          }
+        })
 
-      spyOn(virtualElement, 'getBoundingClientRect').and.callThrough()
+        spyOn(virtualElement, 'getBoundingClientRect').and.callThrough()
 
-      dropdown.toggle()
+        dropdown.toggle()
+      })
     })
 
-    it('should not toggle a dropdown if the element is disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not toggle a dropdown if the element is disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        })
       })
     })
 
-    it('should not toggle a dropdown if the element contains .disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not toggle a dropdown if the element contains .disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        })
       })
     })
 
-    it('should not toggle a dropdown if the menu is shown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not toggle a dropdown if the menu is shown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        })
       })
     })
 
-    it('should not toggle a dropdown if show event is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not toggle a dropdown if show event is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('show.bs.dropdown', event => {
-        event.preventDefault()
-      })
+        btnDropdown.addEventListener('show.bs.dropdown', event => {
+          event.preventDefault()
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.toggle()
+        dropdown.toggle()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        })
       })
     })
   })
 
   describe('show', () => {
-    it('should show a dropdown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+    it('should show a dropdown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
+
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
+          resolve()
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
-        done()
+        dropdown.show()
       })
-
-      dropdown.show()
     })
 
-    it('should not show a dropdown if the element is disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not show a dropdown if the element is disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.show()
+        dropdown.show()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not show a dropdown if the element contains .disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not show a dropdown if the element contains .disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.show()
+        dropdown.show()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not show a dropdown if the menu is shown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not show a dropdown if the menu is shown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.show()
+        dropdown.show()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not show a dropdown if show event is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not show a dropdown if show event is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('show.bs.dropdown', event => {
-        event.preventDefault()
-      })
+        btnDropdown.addEventListener('show.bs.dropdown', event => {
+          event.preventDefault()
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        throw new Error('should not throw shown.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          throw new Error('should not throw shown.bs.dropdown event')
+        })
 
-      dropdown.show()
+        dropdown.show()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
   })
 
   describe('hide', () => {
-    it('should hide a dropdown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="true">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+    it('should hide a dropdown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="true">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
+
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(dropdownMenu).not.toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+          resolve()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(dropdownMenu).not.toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
-        done()
+        dropdown.hide()
       })
-
-      dropdown.hide()
     })
 
-    it('should hide a dropdown and destroy popper', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should hide a dropdown and destroy popper', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          spyOn(dropdown._popper, 'destroy')
+          dropdown.hide()
+        })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        spyOn(dropdown._popper, 'destroy')
-        dropdown.hide()
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(dropdown._popper.destroy).toHaveBeenCalled()
+          resolve()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(dropdown._popper.destroy).toHaveBeenCalled()
-        done()
+        dropdown.show()
       })
-
-      dropdown.show()
     })
 
-    it('should not hide a dropdown if the element is disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not hide a dropdown if the element is disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        throw new Error('should not throw hidden.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          throw new Error('should not throw hidden.bs.dropdown event')
+        })
 
-      dropdown.hide()
+        dropdown.hide()
 
-      setTimeout(() => {
-        expect(dropdownMenu).toHaveClass('show')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(dropdownMenu).toHaveClass('show')
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not hide a dropdown if the element contains .disabled', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not hide a dropdown if the element contains .disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        throw new Error('should not throw hidden.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          throw new Error('should not throw hidden.bs.dropdown event')
+        })
 
-      dropdown.hide()
+        dropdown.hide()
 
-      setTimeout(() => {
-        expect(dropdownMenu).toHaveClass('show')
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect(dropdownMenu).toHaveClass('show')
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not hide a dropdown if the menu is not shown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not hide a dropdown if the menu is not shown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        throw new Error('should not throw hidden.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          throw new Error('should not throw hidden.bs.dropdown event')
+        })
 
-      dropdown.hide()
+        dropdown.hide()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should not hide a dropdown if hide event is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu show">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not hide a dropdown if hide event is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu show">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('hide.bs.dropdown', event => {
-        event.preventDefault()
-      })
+        btnDropdown.addEventListener('hide.bs.dropdown', event => {
+          event.preventDefault()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        throw new Error('should not throw hidden.bs.dropdown event')
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          throw new Error('should not throw hidden.bs.dropdown event')
+        })
 
-      dropdown.hide()
+        dropdown.hide()
 
-      setTimeout(() => {
-        expect(dropdownMenu).toHaveClass('show')
-        done()
+        setTimeout(() => {
+          expect(dropdownMenu).toHaveClass('show')
+          resolve()
+        })
       })
     })
 
-    it('should remove event listener on touch-enabled device that was added in show method', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Dropdwon item</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should remove event listener on touch-enabled device that was added in show method', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Dropdwon item</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const defaultValueOnTouchStart = document.documentElement.ontouchstart
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown)
+        const defaultValueOnTouchStart = document.documentElement.ontouchstart
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(btnDropdown)
 
-      document.documentElement.ontouchstart = noop
-      spyOn(EventHandler, 'off')
+        document.documentElement.ontouchstart = noop
+        spyOn(EventHandler, 'off')
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        dropdown.hide()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          dropdown.hide()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(btnDropdown).not.toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
-        expect(EventHandler.off).toHaveBeenCalled()
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(btnDropdown).not.toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+          expect(EventHandler.off).toHaveBeenCalled()
 
-        document.documentElement.ontouchstart = defaultValueOnTouchStart
-        done()
-      })
+          document.documentElement.ontouchstart = defaultValueOnTouchStart
+          resolve()
+        })
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
   })
 
@@ -1013,903 +1071,957 @@ describe('Dropdown', () => {
   })
 
   describe('data-api', () => {
-    it('should show and hide a dropdown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      let showEventTriggered = false
-      let hideEventTriggered = false
+    it('should show and hide a dropdown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        let showEventTriggered = false
+        let hideEventTriggered = false
+
+        btnDropdown.addEventListener('show.bs.dropdown', () => {
+          showEventTriggered = true
+        })
 
-      btnDropdown.addEventListener('show.bs.dropdown', () => {
-        showEventTriggered = true
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', event => setTimeout(() => {
+          expect(btnDropdown).toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+          expect(showEventTriggered).toBeTrue()
+          expect(event.relatedTarget).toEqual(btnDropdown)
+          document.body.click()
+        }))
 
-      btnDropdown.addEventListener('shown.bs.dropdown', event => setTimeout(() => {
-        expect(btnDropdown).toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-        expect(showEventTriggered).toBeTrue()
-        expect(event.relatedTarget).toEqual(btnDropdown)
-        document.body.click()
-      }))
+        btnDropdown.addEventListener('hide.bs.dropdown', () => {
+          hideEventTriggered = true
+        })
 
-      btnDropdown.addEventListener('hide.bs.dropdown', () => {
-        hideEventTriggered = true
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', event => {
+          expect(btnDropdown).not.toHaveClass('show')
+          expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+          expect(hideEventTriggered).toBeTrue()
+          expect(event.relatedTarget).toEqual(btnDropdown)
+          resolve()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', event => {
-        expect(btnDropdown).not.toHaveClass('show')
-        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
-        expect(hideEventTriggered).toBeTrue()
-        expect(event.relatedTarget).toEqual(btnDropdown)
-        done()
+        btnDropdown.click()
       })
-
-      btnDropdown.click()
     })
 
-    it('should not use "static" Popper in navbar', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar navbar-expand-md navbar-light bg-light">',
-        '  <div class="dropdown">',
-        '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '    <div class="dropdown-menu">',
-        '      <a class="dropdown-item" href="#">Secondary link</a>',
-        '    </div>',
-        '  </div>',
-        '</nav>'
-      ].join('')
+    it('should not use "static" Popper in navbar', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar navbar-expand-md navbar-light bg-light">',
+          '  <div class="dropdown">',
+          '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '    <div class="dropdown-menu">',
+          '      <a class="dropdown-item" href="#">Secondary link</a>',
+          '    </div>',
+          '  </div>',
+          '</nav>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(dropdown._popper).not.toBeNull()
-        expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(dropdown._popper).not.toBeNull()
+          expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+          resolve()
+        })
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
 
-    it('should not collapse the dropdown when clicking a select option nested in the dropdown', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <select>',
-        '      <option selected>Open this select menu</option>',
-        '      <option value="1">One</option>',
-        '    </select>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not collapse the dropdown when clicking a select option nested in the dropdown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <select>',
+          '      <option selected>Open this select menu</option>',
+          '      <option value="1">One</option>',
+          '    </select>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
+
+        const hideSpy = spyOn(dropdown, '_completeHide')
 
-      const hideSpy = spyOn(dropdown, '_completeHide')
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          const clickEvent = new MouseEvent('click', {
+            bubbles: true
+          })
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        const clickEvent = new MouseEvent('click', {
-          bubbles: true
+          dropdownMenu.querySelector('option').dispatchEvent(clickEvent)
         })
 
-        dropdownMenu.querySelector('option').dispatchEvent(clickEvent)
-      })
+        dropdownMenu.addEventListener('click', event => {
+          expect(event.target.tagName).toMatch(/select|option/i)
 
-      dropdownMenu.addEventListener('click', event => {
-        expect(event.target.tagName).toMatch(/select|option/i)
+          Dropdown.clearMenus(event)
 
-        Dropdown.clearMenus(event)
+          setTimeout(() => {
+            expect(hideSpy).not.toHaveBeenCalled()
+            resolve()
+          }, 10)
+        })
 
-        setTimeout(() => {
-          expect(hideSpy).not.toHaveBeenCalled()
-          done()
-        }, 10)
+        dropdown.show()
       })
-
-      dropdown.show()
     })
 
-    it('should manage bs attribute `data-bs-popper`="static" when dropdown is in navbar', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar navbar-expand-md navbar-light bg-light">',
-        '  <div class="dropdown">',
-        '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
-        '    <div class="dropdown-menu">',
-        '      <a class="dropdown-item" href="#">Secondary link</a>',
-        '    </div>',
-        '  </div>',
-        '</nav>'
-      ].join('')
+    it('should manage bs attribute `data-bs-popper`="static" when dropdown is in navbar', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar navbar-expand-md navbar-light bg-light">',
+          '  <div class="dropdown">',
+          '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+          '    <div class="dropdown-menu">',
+          '      <a class="dropdown-item" href="#">Secondary link</a>',
+          '    </div>',
+          '  </div>',
+          '</nav>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
-        dropdown.hide()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+          dropdown.hide()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
-        done()
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
+          resolve()
+        })
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
 
-    it('should not use Popper if display set to static', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not use Popper if display set to static', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        // Popper adds this attribute when we use it
-        expect(dropdownMenu.getAttribute('data-popper-placement')).toBeNull()
-        done()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          // Popper adds this attribute when we use it
+          expect(dropdownMenu.getAttribute('data-popper-placement')).toBeNull()
+          resolve()
+        })
 
-      btnDropdown.click()
+        btnDropdown.click()
+      })
     })
 
-    it('should manage bs attribute `data-bs-popper`="static" when display set to static', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should manage bs attribute `data-bs-popper`="static" when display set to static', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-      const dropdown = new Dropdown(btnDropdown)
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdown = new Dropdown(btnDropdown)
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
-        dropdown.hide()
-      })
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+          dropdown.hide()
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
-        done()
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
+          resolve()
+        })
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
 
-    it('should remove "show" class if tabbing outside of menu', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should remove "show" class if tabbing outside of menu', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Secondary link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
 
-      btnDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(btnDropdown).toHaveClass('show')
+        btnDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(btnDropdown).toHaveClass('show')
 
-        const keyup = createEvent('keyup')
+          const keyup = createEvent('keyup')
 
-        keyup.key = 'Tab'
-        document.dispatchEvent(keyup)
-      })
+          keyup.key = 'Tab'
+          document.dispatchEvent(keyup)
+        })
 
-      btnDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect(btnDropdown).not.toHaveClass('show')
-        done()
-      })
+        btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect(btnDropdown).not.toHaveClass('show')
+          resolve()
+        })
 
-      btnDropdown.click()
+        btnDropdown.click()
+      })
     })
 
-    it('should remove "show" class if body is clicked, with multiple dropdowns', done => {
-      fixtureEl.innerHTML = [
-        '<div class="nav">',
-        '  <div class="dropdown" id="testmenu">',
-        '    <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
-        '    <div class="dropdown-menu">',
-        '      <a class="dropdown-item" href="#sub1">Submenu 1</a>',
-        '    </div>',
-        '  </div>',
-        '</div>',
-        '<div class="btn-group">',
-        '  <button class="btn">Actions</button>',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Action 1</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should remove "show" class if body is clicked, with multiple dropdowns', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="nav">',
+          '  <div class="dropdown" id="testmenu">',
+          '    <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
+          '    <div class="dropdown-menu">',
+          '      <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+          '    </div>',
+          '  </div>',
+          '</div>',
+          '<div class="btn-group">',
+          '  <button class="btn">Actions</button>',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Action 1</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
+        const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
 
-      expect(triggerDropdownList).toHaveSize(2)
+        expect(triggerDropdownList).toHaveSize(2)
 
-      const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
+        const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
 
-      triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdownFirst).toHaveClass('show')
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
-        document.body.click()
-      })
+        triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdownFirst).toHaveClass('show')
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+          document.body.click()
+        })
 
-      triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
-        triggerDropdownLast.click()
-      })
+        triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+          triggerDropdownLast.click()
+        })
 
-      triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdownLast).toHaveClass('show')
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
-        document.body.click()
-      })
+        triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdownLast).toHaveClass('show')
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+          document.body.click()
+        })
 
-      triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
-        done()
-      })
+        triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+          resolve()
+        })
 
-      triggerDropdownFirst.click()
+        triggerDropdownFirst.click()
+      })
     })
 
-    it('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
-        '  </div>',
-        '</div>',
-        '<div class="btn-group">',
-        '  <button class="btn">Actions</button>',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Action 1</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+          '  </div>',
+          '</div>',
+          '<div class="btn-group">',
+          '  <button class="btn">Actions</button>',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Action 1</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
+        const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
 
-      expect(triggerDropdownList).toHaveSize(2)
+        expect(triggerDropdownList).toHaveSize(2)
 
-      const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
+        const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
 
-      triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdownFirst).toHaveClass('show')
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+        triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdownFirst).toHaveClass('show')
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
 
-        const keyup = createEvent('keyup')
-        keyup.key = 'Tab'
+          const keyup = createEvent('keyup')
+          keyup.key = 'Tab'
 
-        document.dispatchEvent(keyup)
-      })
+          document.dispatchEvent(keyup)
+        })
 
-      triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
-        triggerDropdownLast.click()
-      })
+        triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+          triggerDropdownLast.click()
+        })
 
-      triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdownLast).toHaveClass('show')
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+        triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdownLast).toHaveClass('show')
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
 
-        const keyup = createEvent('keyup')
-        keyup.key = 'Tab'
+          const keyup = createEvent('keyup')
+          keyup.key = 'Tab'
 
-        document.dispatchEvent(keyup)
-      })
+          document.dispatchEvent(keyup)
+        })
 
-      triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
-        expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
-        done()
-      })
+        triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
+          expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+          resolve()
+        })
 
-      triggerDropdownFirst.click()
+        triggerDropdownFirst.click()
+      })
     })
 
-    it('should fire hide and hidden event without a clickEvent if event type is not click', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should fire hide and hidden event without a clickEvent if event type is not click', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
 
-      triggerDropdown.addEventListener('hide.bs.dropdown', event => {
-        expect(event.clickEvent).toBeUndefined()
-      })
+        triggerDropdown.addEventListener('hide.bs.dropdown', event => {
+          expect(event.clickEvent).toBeUndefined()
+        })
 
-      triggerDropdown.addEventListener('hidden.bs.dropdown', event => {
-        expect(event.clickEvent).toBeUndefined()
-        done()
-      })
+        triggerDropdown.addEventListener('hidden.bs.dropdown', event => {
+          expect(event.clickEvent).toBeUndefined()
+          resolve()
+        })
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        const keydown = createEvent('keydown')
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          const keydown = createEvent('keydown')
 
-        keydown.key = 'Escape'
-        triggerDropdown.dispatchEvent(keydown)
-      })
+          keydown.key = 'Escape'
+          triggerDropdown.dispatchEvent(keydown)
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should bubble up the events to the parent elements', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#subMenu">Sub menu</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should bubble up the events to the parent elements', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#subMenu">Sub menu</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownParent = fixtureEl.querySelector('.dropdown')
-      const dropdown = new Dropdown(triggerDropdown)
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownParent = fixtureEl.querySelector('.dropdown')
+        const dropdown = new Dropdown(triggerDropdown)
 
-      const showFunction = jasmine.createSpy('showFunction')
-      dropdownParent.addEventListener('show.bs.dropdown', showFunction)
+        const showFunction = jasmine.createSpy('showFunction')
+        dropdownParent.addEventListener('show.bs.dropdown', showFunction)
 
-      const shownFunction = jasmine.createSpy('shownFunction')
-      dropdownParent.addEventListener('shown.bs.dropdown', () => {
-        shownFunction()
-        dropdown.hide()
-      })
+        const shownFunction = jasmine.createSpy('shownFunction')
+        dropdownParent.addEventListener('shown.bs.dropdown', () => {
+          shownFunction()
+          dropdown.hide()
+        })
 
-      const hideFunction = jasmine.createSpy('hideFunction')
-      dropdownParent.addEventListener('hide.bs.dropdown', hideFunction)
+        const hideFunction = jasmine.createSpy('hideFunction')
+        dropdownParent.addEventListener('hide.bs.dropdown', hideFunction)
 
-      dropdownParent.addEventListener('hidden.bs.dropdown', () => {
-        expect(showFunction).toHaveBeenCalled()
-        expect(shownFunction).toHaveBeenCalled()
-        expect(hideFunction).toHaveBeenCalled()
-        done()
-      })
+        dropdownParent.addEventListener('hidden.bs.dropdown', () => {
+          expect(showFunction).toHaveBeenCalled()
+          expect(shownFunction).toHaveBeenCalled()
+          expect(hideFunction).toHaveBeenCalled()
+          resolve()
+        })
 
-      dropdown.show()
+        dropdown.show()
+      })
     })
 
-    it('should ignore keyboard events within <input>s and <textarea>s', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
-        '    <input type="text">',
-        '    <textarea></textarea>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should ignore keyboard events within <input>s and <textarea>s', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+          '    <input type="text">',
+          '    <textarea></textarea>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const input = fixtureEl.querySelector('input')
-      const textarea = fixtureEl.querySelector('textarea')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const input = fixtureEl.querySelector('input')
+        const textarea = fixtureEl.querySelector('textarea')
+
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          input.focus()
+          const keydown = createEvent('keydown')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        input.focus()
-        const keydown = createEvent('keydown')
+          keydown.key = 'ArrowUp'
+          input.dispatchEvent(keydown)
 
-        keydown.key = 'ArrowUp'
-        input.dispatchEvent(keydown)
+          expect(document.activeElement).toEqual(input, 'input still focused')
 
-        expect(document.activeElement).toEqual(input, 'input still focused')
+          textarea.focus()
+          textarea.dispatchEvent(keydown)
 
-        textarea.focus()
-        textarea.dispatchEvent(keydown)
+          expect(document.activeElement).toEqual(textarea, 'textarea still focused')
+          resolve()
+        })
 
-        expect(document.activeElement).toEqual(textarea, 'textarea still focused')
-        done()
+        triggerDropdown.click()
       })
-
-      triggerDropdown.click()
     })
 
-    it('should skip disabled element when using keyboard navigation', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
-        '    <button class="dropdown-item" type="button" disabled>Disabled button</button>',
-        '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should skip disabled element when using keyboard navigation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
+          '    <button class="dropdown-item" type="button" disabled>Disabled button</button>',
+          '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        const keydown = createEvent('keydown')
-        keydown.key = 'ArrowDown'
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          const keydown = createEvent('keydown')
+          keydown.key = 'ArrowDown'
 
-        triggerDropdown.dispatchEvent(keydown)
-        triggerDropdown.dispatchEvent(keydown)
+          triggerDropdown.dispatchEvent(keydown)
+          triggerDropdown.dispatchEvent(keydown)
 
-        expect(document.activeElement).not.toHaveClass('disabled')
-        expect(document.activeElement.hasAttribute('disabled')).toBeFalse()
-        done()
-      })
+          expect(document.activeElement).not.toHaveClass('disabled')
+          expect(document.activeElement.hasAttribute('disabled')).toBeFalse()
+          resolve()
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should skip hidden element when using keyboard navigation', done => {
-      fixtureEl.innerHTML = [
-        '<style>',
-        '  .d-none {',
-        '    display: none;',
-        '  }',
-        '</style>',
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <button class="dropdown-item d-none" type="button">Hidden button by class</button>',
-        '    <a class="dropdown-item" href="#sub1" style="display: none">Hidden link</a>',
-        '    <a class="dropdown-item" href="#sub1" style="visibility: hidden">Hidden link</a>',
-        '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should skip hidden element when using keyboard navigation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<style>',
+          '  .d-none {',
+          '    display: none;',
+          '  }',
+          '</style>',
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <button class="dropdown-item d-none" type="button">Hidden button by class</button>',
+          '    <a class="dropdown-item" href="#sub1" style="display: none">Hidden link</a>',
+          '    <a class="dropdown-item" href="#sub1" style="visibility: hidden">Hidden link</a>',
+          '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        const keydown = createEvent('keydown')
-        keydown.key = 'ArrowDown'
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          const keydown = createEvent('keydown')
+          keydown.key = 'ArrowDown'
 
-        triggerDropdown.dispatchEvent(keydown)
+          triggerDropdown.dispatchEvent(keydown)
 
-        expect(document.activeElement).not.toHaveClass('d-none')
-        expect(document.activeElement.style.display).not.toEqual('none')
-        expect(document.activeElement.style.visibility).not.toEqual('hidden')
+          expect(document.activeElement).not.toHaveClass('d-none')
+          expect(document.activeElement.style.display).not.toEqual('none')
+          expect(document.activeElement.style.visibility).not.toEqual('hidden')
 
-        done()
-      })
+          resolve()
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should focus next/previous element when using keyboard navigation', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a id="item1" class="dropdown-item" href="#">A link</a>',
-        '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should focus next/previous element when using keyboard navigation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a id="item1" class="dropdown-item" href="#">A link</a>',
+          '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const item1 = fixtureEl.querySelector('#item1')
-      const item2 = fixtureEl.querySelector('#item2')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const item1 = fixtureEl.querySelector('#item1')
+        const item2 = fixtureEl.querySelector('#item2')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        const keydownArrowDown = createEvent('keydown')
-        keydownArrowDown.key = 'ArrowDown'
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          const keydownArrowDown = createEvent('keydown')
+          keydownArrowDown.key = 'ArrowDown'
 
-        triggerDropdown.dispatchEvent(keydownArrowDown)
-        expect(document.activeElement).toEqual(item1, 'item1 is focused')
+          triggerDropdown.dispatchEvent(keydownArrowDown)
+          expect(document.activeElement).toEqual(item1, 'item1 is focused')
 
-        document.activeElement.dispatchEvent(keydownArrowDown)
-        expect(document.activeElement).toEqual(item2, 'item2 is focused')
+          document.activeElement.dispatchEvent(keydownArrowDown)
+          expect(document.activeElement).toEqual(item2, 'item2 is focused')
 
-        const keydownArrowUp = createEvent('keydown')
-        keydownArrowUp.key = 'ArrowUp'
+          const keydownArrowUp = createEvent('keydown')
+          keydownArrowUp.key = 'ArrowUp'
 
-        document.activeElement.dispatchEvent(keydownArrowUp)
-        expect(document.activeElement).toEqual(item1, 'item1 is focused')
+          document.activeElement.dispatchEvent(keydownArrowUp)
+          expect(document.activeElement).toEqual(item1, 'item1 is focused')
 
-        done()
-      })
+          resolve()
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should open the dropdown and focus on the last item when using ArrowUp for the first time', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a id="item1" class="dropdown-item" href="#">A link</a>',
-        '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should open the dropdown and focus on the last item when using ArrowUp for the first time', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a id="item1" class="dropdown-item" href="#">A link</a>',
+          '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const lastItem = fixtureEl.querySelector('#item2')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const lastItem = fixtureEl.querySelector('#item2')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        setTimeout(() => {
-          expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
-          done()
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          setTimeout(() => {
+            expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
+            resolve()
+          })
         })
-      })
 
-      const keydown = createEvent('keydown')
-      keydown.key = 'ArrowUp'
-      triggerDropdown.dispatchEvent(keydown)
+        const keydown = createEvent('keydown')
+        keydown.key = 'ArrowUp'
+        triggerDropdown.dispatchEvent(keydown)
+      })
     })
 
-    it('should open the dropdown and focus on the first item when using ArrowDown for the first time', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a id="item1" class="dropdown-item" href="#">A link</a>',
-        '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should open the dropdown and focus on the first item when using ArrowDown for the first time', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a id="item1" class="dropdown-item" href="#">A link</a>',
+          '    <a id="item2" class="dropdown-item" href="#">Another link</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const firstItem = fixtureEl.querySelector('#item1')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const firstItem = fixtureEl.querySelector('#item1')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        setTimeout(() => {
-          expect(document.activeElement).toEqual(firstItem, 'item1 is focused')
-          done()
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          setTimeout(() => {
+            expect(document.activeElement).toEqual(firstItem, 'item1 is focused')
+            resolve()
+          })
         })
-      })
 
-      const keydown = createEvent('keydown')
-      keydown.key = 'ArrowDown'
-      triggerDropdown.dispatchEvent(keydown)
+        const keydown = createEvent('keydown')
+        keydown.key = 'ArrowDown'
+        triggerDropdown.dispatchEvent(keydown)
+      })
     })
 
-    it('should not close the dropdown if the user clicks on a text field within dropdown-menu', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <input type="text">',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not close the dropdown if the user clicks on a text field within dropdown-menu', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <input type="text">',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const input = fixtureEl.querySelector('input')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const input = fixtureEl.querySelector('input')
 
-      input.addEventListener('click', () => {
-        expect(triggerDropdown).toHaveClass('show')
-        done()
-      })
+        input.addEventListener('click', () => {
+          expect(triggerDropdown).toHaveClass('show')
+          resolve()
+        })
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdown).toHaveClass('show')
-        input.dispatchEvent(createEvent('click'))
-      })
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdown).toHaveClass('show')
+          input.dispatchEvent(createEvent('click'))
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should not close the dropdown if the user clicks on a textarea within dropdown-menu', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <textarea></textarea>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not close the dropdown if the user clicks on a textarea within dropdown-menu', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <textarea></textarea>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const textarea = fixtureEl.querySelector('textarea')
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const textarea = fixtureEl.querySelector('textarea')
 
-      textarea.addEventListener('click', () => {
-        expect(triggerDropdown).toHaveClass('show')
-        done()
-      })
+        textarea.addEventListener('click', () => {
+          expect(triggerDropdown).toHaveClass('show')
+          resolve()
+        })
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        expect(triggerDropdown).toHaveClass('show')
-        textarea.dispatchEvent(createEvent('click'))
-      })
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          expect(triggerDropdown).toHaveClass('show')
+          textarea.dispatchEvent(createEvent('click'))
+        })
 
-      triggerDropdown.click()
+        triggerDropdown.click()
+      })
     })
 
-    it('should close the dropdown if the user clicks on a text field that is not contained within dropdown-menu', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '  </div>',
-        '</div>',
-        '<input type="text">'
-      ].join('')
-
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const input = fixtureEl.querySelector('input')
+    it('should close the dropdown if the user clicks on a text field that is not contained within dropdown-menu', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '  </div>',
+          '</div>',
+          '<input type="text">'
+        ].join('')
 
-      triggerDropdown.addEventListener('hidden.bs.dropdown', () => {
-        expect().nothing()
-        done()
-      })
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const input = fixtureEl.querySelector('input')
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        input.dispatchEvent(createEvent('click', {
-          bubbles: true
-        }))
-      })
+        triggerDropdown.addEventListener('hidden.bs.dropdown', () => {
+          expect().nothing()
+          resolve()
+        })
 
-      triggerDropdown.click()
-    })
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          input.dispatchEvent(createEvent('click', {
+            bubbles: true
+          }))
+        })
 
-    it('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
-        '    <input type="text">',
-        '    <textarea></textarea>',
-        '  </div>',
-        '</div>'
-      ].join('')
+        triggerDropdown.click()
+      })
+    })
+
+    it('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+          '    <input type="text">',
+          '    <textarea></textarea>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const input = fixtureEl.querySelector('input')
+        const textarea = fixtureEl.querySelector('textarea')
+
+        const test = (eventKey, elementToDispatch) => {
+          const event = createEvent('keydown')
+          event.key = eventKey
+          elementToDispatch.focus()
+          elementToDispatch.dispatchEvent(event)
+          expect(document.activeElement).toEqual(elementToDispatch, `${elementToDispatch.tagName} still focused`)
+        }
 
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const input = fixtureEl.querySelector('input')
-      const textarea = fixtureEl.querySelector('textarea')
-
-      const test = (eventKey, elementToDispatch) => {
-        const event = createEvent('keydown')
-        event.key = eventKey
-        elementToDispatch.focus()
-        elementToDispatch.dispatchEvent(event)
-        expect(document.activeElement).toEqual(elementToDispatch, `${elementToDispatch.tagName} still focused`)
-      }
+        const keydownEscape = createEvent('keydown')
+        keydownEscape.key = 'Escape'
 
-      const keydownEscape = createEvent('keydown')
-      keydownEscape.key = 'Escape'
+        triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+          // Key Space
+          test('Space', input)
 
-      triggerDropdown.addEventListener('shown.bs.dropdown', () => {
-        // Key Space
-        test('Space', input)
+          test('Space', textarea)
 
-        test('Space', textarea)
+          // Key ArrowUp
+          test('ArrowUp', input)
 
-        // Key ArrowUp
-        test('ArrowUp', input)
+          test('ArrowUp', textarea)
 
-        test('ArrowUp', textarea)
+          // Key ArrowDown
+          test('ArrowDown', input)
 
-        // Key ArrowDown
-        test('ArrowDown', input)
+          test('ArrowDown', textarea)
 
-        test('ArrowDown', textarea)
+          // Key Escape
+          input.focus()
+          input.dispatchEvent(keydownEscape)
 
-        // Key Escape
-        input.focus()
-        input.dispatchEvent(keydownEscape)
+          expect(triggerDropdown).not.toHaveClass('show')
+          resolve()
+        })
 
-        expect(triggerDropdown).not.toHaveClass('show')
-        done()
+        triggerDropdown.click()
       })
-
-      triggerDropdown.click()
     })
 
-    it('should not open dropdown if escape key was pressed on the toggle', done => {
-      fixtureEl.innerHTML = [
-        '<div class="tabs">',
-        '  <div class="dropdown">',
-        '    <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '    <div class="dropdown-menu">',
-        '      <a class="dropdown-item" href="#">Secondary link</a>',
-        '      <a class="dropdown-item" href="#">Something else here</a>',
-        '      <div class="divider"></div>',
-        '      <a class="dropdown-item" href="#">Another link</a>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(triggerDropdown)
-      const button = fixtureEl.querySelector('button[data-bs-toggle="dropdown"]')
-
-      spyOn(dropdown, 'toggle')
+    it('should not open dropdown if escape key was pressed on the toggle', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="tabs">',
+          '  <div class="dropdown">',
+          '    <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '    <div class="dropdown-menu">',
+          '      <a class="dropdown-item" href="#">Secondary link</a>',
+          '      <a class="dropdown-item" href="#">Something else here</a>',
+          '      <div class="divider"></div>',
+          '      <a class="dropdown-item" href="#">Another link</a>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      // Key escape
-      button.focus()
-      // Key escape
-      const keydownEscape = createEvent('keydown')
-      keydownEscape.key = 'Escape'
-      button.dispatchEvent(keydownEscape)
+        const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdown = new Dropdown(triggerDropdown)
+        const button = fixtureEl.querySelector('button[data-bs-toggle="dropdown"]')
 
-      setTimeout(() => {
-        expect(dropdown.toggle).not.toHaveBeenCalled()
-        expect(triggerDropdown).not.toHaveClass('show')
-        done()
-      }, 20)
-    })
+        spyOn(dropdown, 'toggle')
 
-    it('should propagate escape key events if dropdown is closed', done => {
-      fixtureEl.innerHTML = [
-        '<div class="parent">',
-        '  <div class="dropdown">',
-        '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '    <div class="dropdown-menu">',
-        '      <a class="dropdown-item" href="#">Some Item</a>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+        // Key escape
+        button.focus()
+        // Key escape
+        const keydownEscape = createEvent('keydown')
+        keydownEscape.key = 'Escape'
+        button.dispatchEvent(keydownEscape)
 
-      const parent = fixtureEl.querySelector('.parent')
-      const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        setTimeout(() => {
+          expect(dropdown.toggle).not.toHaveBeenCalled()
+          expect(triggerDropdown).not.toHaveClass('show')
+          resolve()
+        }, 20)
+      })
+    })
+
+    it('should propagate escape key events if dropdown is closed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="parent">',
+          '  <div class="dropdown">',
+          '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '    <div class="dropdown-menu">',
+          '      <a class="dropdown-item" href="#">Some Item</a>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const parent = fixtureEl.querySelector('.parent')
+        const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+
+        const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
+
+        parent.addEventListener('keydown', parentKeyHandler)
+        parent.addEventListener('keyup', () => {
+          expect(parentKeyHandler).toHaveBeenCalled()
+          resolve()
+        })
 
-      const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
+        const keydownEscape = createEvent('keydown', { bubbles: true })
+        keydownEscape.key = 'Escape'
+        const keyupEscape = createEvent('keyup', { bubbles: true })
+        keyupEscape.key = 'Escape'
 
-      parent.addEventListener('keydown', parentKeyHandler)
-      parent.addEventListener('keyup', () => {
-        expect(parentKeyHandler).toHaveBeenCalled()
-        done()
+        toggle.focus()
+        toggle.dispatchEvent(keydownEscape)
+        toggle.dispatchEvent(keyupEscape)
       })
-
-      const keydownEscape = createEvent('keydown', { bubbles: true })
-      keydownEscape.key = 'Escape'
-      const keyupEscape = createEvent('keyup', { bubbles: true })
-      keyupEscape.key = 'Escape'
-
-      toggle.focus()
-      toggle.dispatchEvent(keydownEscape)
-      toggle.dispatchEvent(keyupEscape)
     })
 
-    it('should close dropdown using `escape` button, and return focus to its trigger', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Some Item</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should close dropdown using `escape` button, and return focus to its trigger', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Some Item</a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
 
-      toggle.addEventListener('shown.bs.dropdown', () => {
-        const keydownEvent = createEvent('keydown', { bubbles: true })
-        keydownEvent.key = 'ArrowDown'
-        toggle.dispatchEvent(keydownEvent)
-        keydownEvent.key = 'Escape'
-        toggle.dispatchEvent(keydownEvent)
-      })
+        toggle.addEventListener('shown.bs.dropdown', () => {
+          const keydownEvent = createEvent('keydown', { bubbles: true })
+          keydownEvent.key = 'ArrowDown'
+          toggle.dispatchEvent(keydownEvent)
+          keydownEvent.key = 'Escape'
+          toggle.dispatchEvent(keydownEvent)
+        })
 
-      toggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
-        expect(document.activeElement).toEqual(toggle)
-        done()
-      }))
+        toggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
+          expect(document.activeElement).toEqual(toggle)
+          resolve()
+        }))
 
-      toggle.click()
+        toggle.click()
+      })
     })
 
-    it('should close dropdown (only) by clicking inside the dropdown menu when it has data-attribute `data-bs-auto-close="inside"`', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="inside">Dropdown toggle</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Dropdown item</a>',
-        ' </div>',
-        '</div>'
-      ].join('')
+    it('should close dropdown (only) by clicking inside the dropdown menu when it has data-attribute `data-bs-auto-close="inside"`', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="inside">Dropdown toggle</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Dropdown item</a>',
+          ' </div>',
+          '</div>'
+        ].join('')
 
-      const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
 
-      const expectDropdownToBeOpened = () => setTimeout(() => {
-        expect(dropdownToggle).toHaveClass('show')
-        dropdownMenu.click()
-      }, 150)
+        const expectDropdownToBeOpened = () => setTimeout(() => {
+          expect(dropdownToggle).toHaveClass('show')
+          dropdownMenu.click()
+        }, 150)
 
-      dropdownToggle.addEventListener('shown.bs.dropdown', () => {
-        document.documentElement.click()
-        expectDropdownToBeOpened()
-      })
+        dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+          document.documentElement.click()
+          expectDropdownToBeOpened()
+        })
 
-      dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
-        expect(dropdownToggle).not.toHaveClass('show')
-        done()
-      }))
+        dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
+          expect(dropdownToggle).not.toHaveClass('show')
+          resolve()
+        }))
 
-      dropdownToggle.click()
+        dropdownToggle.click()
+      })
     })
 
-    it('should close dropdown (only) by clicking outside the dropdown menu when it has data-attribute `data-bs-auto-close="outside"`', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown toggle</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Dropdown item</a>',
-        ' </div>',
-        '</div>'
-      ].join('')
+    it('should close dropdown (only) by clicking outside the dropdown menu when it has data-attribute `data-bs-auto-close="outside"`', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown toggle</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Dropdown item</a>',
+          ' </div>',
+          '</div>'
+        ].join('')
 
-      const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
 
-      const expectDropdownToBeOpened = () => setTimeout(() => {
-        expect(dropdownToggle).toHaveClass('show')
-        document.documentElement.click()
-      }, 150)
+        const expectDropdownToBeOpened = () => setTimeout(() => {
+          expect(dropdownToggle).toHaveClass('show')
+          document.documentElement.click()
+        }, 150)
 
-      dropdownToggle.addEventListener('shown.bs.dropdown', () => {
-        dropdownMenu.click()
-        expectDropdownToBeOpened()
-      })
+        dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+          dropdownMenu.click()
+          expectDropdownToBeOpened()
+        })
 
-      dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
-        expect(dropdownToggle).not.toHaveClass('show')
-        done()
-      })
+        dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
+          expect(dropdownToggle).not.toHaveClass('show')
+          resolve()
+        })
 
-      dropdownToggle.click()
+        dropdownToggle.click()
+      })
     })
 
-    it('should not close dropdown by clicking inside or outside the dropdown menu when it has data-attribute `data-bs-auto-close="false"`', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="false">Dropdown toggle</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Dropdown item</a>',
-        ' </div>',
-        '</div>'
-      ].join('')
+    it('should not close dropdown by clicking inside or outside the dropdown menu when it has data-attribute `data-bs-auto-close="false"`', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="dropdown">',
+          '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="false">Dropdown toggle</button>',
+          '  <div class="dropdown-menu">',
+          '    <a class="dropdown-item" href="#">Dropdown item</a>',
+          ' </div>',
+          '</div>'
+        ].join('')
 
-      const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+        const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+        const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
 
-      const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
-        expect(dropdownToggle).toHaveClass('show')
-        if (shouldTriggerClick) {
-          document.documentElement.click()
-        } else {
-          done()
-        }
+        const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
+          expect(dropdownToggle).toHaveClass('show')
+          if (shouldTriggerClick) {
+            document.documentElement.click()
+          } else {
+            resolve()
+          }
 
-        expectDropdownToBeOpened(false)
-      }, 150)
+          expectDropdownToBeOpened(false)
+        }, 150)
 
-      dropdownToggle.addEventListener('shown.bs.dropdown', () => {
-        dropdownMenu.click()
-        expectDropdownToBeOpened()
-      })
+        dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+          dropdownMenu.click()
+          expectDropdownToBeOpened()
+        })
 
-      dropdownToggle.click()
+        dropdownToggle.click()
+      })
     })
   })
 
@@ -2030,52 +2142,54 @@ describe('Dropdown', () => {
     })
   })
 
-  it('should open dropdown when pressing keydown or keyup', done => {
-    fixtureEl.innerHTML = [
-      '<div class="dropdown">',
-      '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-      '  <div class="dropdown-menu">',
-      '    <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
-      '    <button class="dropdown-item" type="button" disabled>Disabled button</button>',
-      '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
-      '  </div>',
-      '</div>'
-    ].join('')
+  it('should open dropdown when pressing keydown or keyup', () => {
+    return new Promise(resolve => {
+      fixtureEl.innerHTML = [
+        '<div class="dropdown">',
+        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+        '  <div class="dropdown-menu">',
+        '    <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
+        '    <button class="dropdown-item" type="button" disabled>Disabled button</button>',
+        '    <a id="item1" class="dropdown-item" href="#">Another link</a>',
+        '  </div>',
+        '</div>'
+      ].join('')
+
+      const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+      const dropdown = fixtureEl.querySelector('.dropdown')
 
-    const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-    const dropdown = fixtureEl.querySelector('.dropdown')
+      const keydown = createEvent('keydown')
+      keydown.key = 'ArrowDown'
 
-    const keydown = createEvent('keydown')
-    keydown.key = 'ArrowDown'
+      const keyup = createEvent('keyup')
+      keyup.key = 'ArrowUp'
 
-    const keyup = createEvent('keyup')
-    keyup.key = 'ArrowUp'
+      const handleArrowDown = () => {
+        expect(triggerDropdown).toHaveClass('show')
+        expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+        setTimeout(() => {
+          dropdown.hide()
+          keydown.key = 'ArrowUp'
+          triggerDropdown.dispatchEvent(keyup)
+        }, 20)
+      }
 
-    const handleArrowDown = () => {
-      expect(triggerDropdown).toHaveClass('show')
-      expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
-      setTimeout(() => {
-        dropdown.hide()
-        keydown.key = 'ArrowUp'
-        triggerDropdown.dispatchEvent(keyup)
-      }, 20)
-    }
-
-    const handleArrowUp = () => {
-      expect(triggerDropdown).toHaveClass('show')
-      expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
-      done()
-    }
-
-    dropdown.addEventListener('shown.bs.dropdown', event => {
-      if (event.target.key === 'ArrowDown') {
-        handleArrowDown()
-      } else {
-        handleArrowUp()
+      const handleArrowUp = () => {
+        expect(triggerDropdown).toHaveClass('show')
+        expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+        resolve()
       }
-    })
 
-    triggerDropdown.dispatchEvent(keydown)
+      dropdown.addEventListener('shown.bs.dropdown', event => {
+        if (event.target.key === 'ArrowDown') {
+          handleArrowDown()
+        } else {
+          handleArrowUp()
+        }
+      })
+
+      triggerDropdown.dispatchEvent(keydown)
+    })
   })
 
   it('should allow `data-bs-toggle="dropdown"` click events to bubble up', () => {
@@ -2101,27 +2215,29 @@ describe('Dropdown', () => {
     expect(delegatedClickListener).toHaveBeenCalled()
   })
 
-  it('should open the dropdown when clicking the child element inside `data-bs-toggle="dropdown"`', done => {
-    fixtureEl.innerHTML = [
-      '<div class="container">',
-      '  <div class="dropdown">',
-      '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown"><span id="childElement">Dropdown</span></button>',
-      '    <div class="dropdown-menu">',
-      '      <a class="dropdown-item" href="#subMenu">Sub menu</a>',
-      '    </div>',
-      '  </div>',
-      '</div>'
-    ].join('')
+  it('should open the dropdown when clicking the child element inside `data-bs-toggle="dropdown"`', () => {
+    return new Promise(resolve => {
+      fixtureEl.innerHTML = [
+        '<div class="container">',
+        '  <div class="dropdown">',
+        '    <button class="btn dropdown-toggle" data-bs-toggle="dropdown"><span id="childElement">Dropdown</span></button>',
+        '    <div class="dropdown-menu">',
+        '      <a class="dropdown-item" href="#subMenu">Sub menu</a>',
+        '    </div>',
+        '  </div>',
+        '</div>'
+      ].join('')
 
-    const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-    const childElement = fixtureEl.querySelector('#childElement')
+      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+      const childElement = fixtureEl.querySelector('#childElement')
 
-    btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
-      expect(btnDropdown).toHaveClass('show')
-      expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
-      done()
-    }))
+      btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
+        expect(btnDropdown).toHaveClass('show')
+        expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+        resolve()
+      }))
 
-    childElement.click()
+      childElement.click()
+    })
   })
 })
index 16781a3518a78e2e311e7e6c0324bb4d65775186..7da39d63038bd1afcab1d38b4a84b281f9f58edd 100644 (file)
@@ -12,7 +12,7 @@ import ScrollSpy from '../../src/scrollspy'
 import Tab from '../../src/tab'
 import Toast from '../../src/toast'
 import Tooltip from '../../src/tooltip'
-import { getFixture, clearFixture } from '../helpers/fixture'
+import { clearFixture, getFixture } from '../helpers/fixture'
 
 describe('jQuery', () => {
   let fixtureEl
@@ -40,19 +40,21 @@ describe('jQuery', () => {
     expect(Tooltip.jQueryInterface).toEqual(jQuery.fn.tooltip)
   })
 
-  it('should use jQuery event system', done => {
-    fixtureEl.innerHTML = [
-      '<div class="alert">',
-      '  <button type="button" data-bs-dismiss="alert">x</button>',
-      '</div>'
-    ].join('')
-
-    $(fixtureEl).find('.alert')
-      .one('closed.bs.alert', () => {
-        expect($(fixtureEl).find('.alert')).toHaveSize(0)
-        done()
-      })
-
-    $(fixtureEl).find('button').trigger('click')
+  it('should use jQuery event system', () => {
+    return new Promise(resolve => {
+      fixtureEl.innerHTML = [
+        '<div class="alert">',
+        '  <button type="button" data-bs-dismiss="alert">x</button>',
+        '</div>'
+      ].join('')
+
+      $(fixtureEl).find('.alert')
+        .one('closed.bs.alert', () => {
+          expect($(fixtureEl).find('.alert')).toHaveSize(0)
+          resolve()
+        })
+
+      $(fixtureEl).find('button').trigger('click')
+    })
   })
 })
index 84a95c86ad53bb5bec07740748a0c45361f94450..bf796411b528b2af4b95690489dbd647bcb8956b 100644 (file)
@@ -56,93 +56,101 @@ describe('Modal', () => {
   })
 
   describe('toggle', () => {
-    it('should call ScrollBarHelper to handle scrollBar on body', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should call ScrollBarHelper to handle scrollBar on body', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+
+        spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+        spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
+          modal.toggle()
+        })
 
-      spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
-      spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
+          resolve()
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
         modal.toggle()
       })
-
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
-        done()
-      })
-
-      modal.toggle()
     })
   })
 
   describe('show', () => {
-    it('should show a modal', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should show a modal', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('show.bs.modal', event => {
-        expect(event).toBeDefined()
-      })
+        modalEl.addEventListener('show.bs.modal', event => {
+          expect(event).toBeDefined()
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toEqual('true')
-        expect(modalEl.getAttribute('role')).toEqual('dialog')
-        expect(modalEl.getAttribute('aria-hidden')).toBeNull()
-        expect(modalEl.style.display).toEqual('block')
-        expect(document.querySelector('.modal-backdrop')).not.toBeNull()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+          expect(modalEl.getAttribute('role')).toEqual('dialog')
+          expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+          expect(modalEl.style.display).toEqual('block')
+          expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should show a modal without backdrop', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should show a modal without backdrop', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        backdrop: false
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          backdrop: false
+        })
 
-      modalEl.addEventListener('show.bs.modal', event => {
-        expect(event).toBeDefined()
-      })
+        modalEl.addEventListener('show.bs.modal', event => {
+          expect(event).toBeDefined()
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toEqual('true')
-        expect(modalEl.getAttribute('role')).toEqual('dialog')
-        expect(modalEl.getAttribute('aria-hidden')).toBeNull()
-        expect(modalEl.style.display).toEqual('block')
-        expect(document.querySelector('.modal-backdrop')).toBeNull()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+          expect(modalEl.getAttribute('role')).toEqual('dialog')
+          expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+          expect(modalEl.style.display).toEqual('block')
+          expect(document.querySelector('.modal-backdrop')).toBeNull()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should show a modal and append the element', done => {
-      const modalEl = document.createElement('div')
-      const id = 'dynamicModal'
+    it('should show a modal and append the element', () => {
+      return new Promise(resolve => {
+        const modalEl = document.createElement('div')
+        const id = 'dynamicModal'
 
-      modalEl.setAttribute('id', id)
-      modalEl.classList.add('modal')
-      modalEl.innerHTML = '<div class="modal-dialog"></div>'
+        modalEl.setAttribute('id', id)
+        modalEl.classList.add('modal')
+        modalEl.innerHTML = '<div class="modal-dialog"></div>'
 
-      const modal = new Modal(modalEl)
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const dynamicModal = document.getElementById(id)
-        expect(dynamicModal).not.toBeNull()
-        dynamicModal.remove()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const dynamicModal = document.getElementById(id)
+          expect(dynamicModal).not.toBeNull()
+          dynamicModal.remove()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
     it('should do nothing if a modal is shown', () => {
@@ -173,511 +181,551 @@ describe('Modal', () => {
       expect(EventHandler.trigger).not.toHaveBeenCalled()
     })
 
-    it('should not fire shown event when show is prevented', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should not fire shown event when show is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('show.bs.modal', event => {
-        event.preventDefault()
+        modalEl.addEventListener('show.bs.modal', event => {
+          event.preventDefault()
 
-        const expectedDone = () => {
-          expect().nothing()
-          done()
-        }
+          const expectedDone = () => {
+            expect().nothing()
+            resolve()
+          }
 
-        setTimeout(expectedDone, 10)
-      })
+          setTimeout(expectedDone, 10)
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        throw new Error('shown event triggered')
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          throw new Error('shown event triggered')
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should be shown after the first call to show() has been prevented while fading is enabled ', done => {
-      fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+    it('should be shown after the first call to show() has been prevented while fading is enabled ', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      let prevented = false
-      modalEl.addEventListener('show.bs.modal', event => {
-        if (!prevented) {
-          event.preventDefault()
-          prevented = true
+        let prevented = false
+        modalEl.addEventListener('show.bs.modal', event => {
+          if (!prevented) {
+            event.preventDefault()
+            prevented = true
 
-          setTimeout(() => {
-            modal.show()
-          })
-        }
-      })
+            setTimeout(() => {
+              modal.show()
+            })
+          }
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(prevented).toBeTrue()
-        expect(modal._isAnimated()).toBeTrue()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(prevented).toBeTrue()
+          expect(modal._isAnimated()).toBeTrue()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
+    it('should set is transitioning if fade class is present', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
 
-    it('should set is transitioning if fade class is present', done => {
-      fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        modalEl.addEventListener('show.bs.modal', () => {
+          setTimeout(() => {
+            expect(modal._isTransitioning).toBeTrue()
+          })
+        })
 
-      modalEl.addEventListener('show.bs.modal', () => {
-        setTimeout(() => {
-          expect(modal._isTransitioning).toBeTrue()
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modal._isTransitioning).toBeFalse()
+          resolve()
         })
-      })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modal._isTransitioning).toBeFalse()
-        done()
+        modal.show()
       })
-
-      modal.show()
     })
 
-    it('should close modal when a click occurred on data-bs-dismiss="modal" inside modal', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal fade">',
-        '  <div class="modal-dialog">',
-        '    <div class="modal-header">',
-        '      <button type="button" data-bs-dismiss="modal"></button>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const modalEl = fixtureEl.querySelector('.modal')
-      const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
-      const modal = new Modal(modalEl)
-
-      spyOn(modal, 'hide').and.callThrough()
+    it('should close modal when a click occurred on data-bs-dismiss="modal" inside modal', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal fade">',
+          '  <div class="modal-dialog">',
+          '    <div class="modal-header">',
+          '      <button type="button" data-bs-dismiss="modal"></button>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
+        const modal = new Modal(modalEl)
+
+        spyOn(modal, 'hide').and.callThrough()
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          btnClose.click()
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        btnClose.click()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modal.hide).toHaveBeenCalled()
+          resolve()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modal.hide).toHaveBeenCalled()
-        done()
+        modal.show()
       })
-
-      modal.show()
     })
 
-    it('should close modal when a click occurred on a data-bs-dismiss="modal" with "bs-target" outside of modal element', done => {
-      fixtureEl.innerHTML = [
-        '<button type="button" data-bs-dismiss="modal" data-bs-target="#modal1"></button>',
-        '<div id="modal1" class="modal fade">',
-        '  <div class="modal-dialog"></div>',
-        '</div>'
-      ].join('')
+    it('should close modal when a click occurred on a data-bs-dismiss="modal" with "bs-target" outside of modal element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button type="button" data-bs-dismiss="modal" data-bs-target="#modal1"></button>',
+          '<div id="modal1" class="modal fade">',
+          '  <div class="modal-dialog"></div>',
+          '</div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
+        const modal = new Modal(modalEl)
 
-      spyOn(modal, 'hide').and.callThrough()
+        spyOn(modal, 'hide').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        btnClose.click()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          btnClose.click()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modal.hide).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modal.hide).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should set .modal\'s scroll top to 0', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal fade">',
-        '  <div class="modal-dialog"></div>',
-        '</div>'
-      ].join('')
+    it('should set .modal\'s scroll top to 0', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal fade">',
+          '  <div class="modal-dialog"></div>',
+          '</div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalEl.scrollTop).toEqual(0)
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalEl.scrollTop).toEqual(0)
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should set modal body scroll top to 0 if modal body do not exists', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal fade">',
-        '  <div class="modal-dialog">',
-        '    <div class="modal-body"></div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modalBody = modalEl.querySelector('.modal-body')
-      const modal = new Modal(modalEl)
+    it('should set modal body scroll top to 0 if modal body do not exists', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal fade">',
+          '  <div class="modal-dialog">',
+          '    <div class="modal-body"></div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modalBody = modalEl.querySelector('.modal-body')
+        const modal = new Modal(modalEl)
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalBody.scrollTop).toEqual(0)
+          resolve()
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalBody.scrollTop).toEqual(0)
-        done()
+        modal.show()
       })
-
-      modal.show()
     })
 
-    it('should not trap focus if focus equal to false', done => {
-      fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+    it('should not trap focus if focus equal to false', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        focus: false
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          focus: false
+        })
 
-      spyOn(modal._focustrap, 'activate').and.callThrough()
+        spyOn(modal._focustrap, 'activate').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modal._focustrap.activate).not.toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modal._focustrap.activate).not.toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should add listener when escape touch is pressed', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should add listener when escape touch is pressed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      spyOn(modal, 'hide').and.callThrough()
+        spyOn(modal, 'hide').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const keydownEscape = createEvent('keydown')
-        keydownEscape.key = 'Escape'
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const keydownEscape = createEvent('keydown')
+          keydownEscape.key = 'Escape'
 
-        modalEl.dispatchEvent(keydownEscape)
-      })
+          modalEl.dispatchEvent(keydownEscape)
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modal.hide).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modal.hide).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should do nothing when the pressed key is not escape', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should do nothing when the pressed key is not escape', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      spyOn(modal, 'hide')
+        spyOn(modal, 'hide')
 
-      const expectDone = () => {
-        expect(modal.hide).not.toHaveBeenCalled()
+        const expectDone = () => {
+          expect(modal.hide).not.toHaveBeenCalled()
 
-        done()
-      }
+          resolve()
+        }
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const keydownTab = createEvent('keydown')
-        keydownTab.key = 'Tab'
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const keydownTab = createEvent('keydown')
+          keydownTab.key = 'Tab'
 
-        modalEl.dispatchEvent(keydownTab)
-        setTimeout(expectDone, 30)
-      })
+          modalEl.dispatchEvent(keydownTab)
+          setTimeout(expectDone, 30)
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should adjust dialog on resize', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should adjust dialog on resize', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
+
+        spyOn(modal, '_adjustDialog').and.callThrough()
 
-      spyOn(modal, '_adjustDialog').and.callThrough()
+        const expectDone = () => {
+          expect(modal._adjustDialog).toHaveBeenCalled()
 
-      const expectDone = () => {
-        expect(modal._adjustDialog).toHaveBeenCalled()
+          resolve()
+        }
 
-        done()
-      }
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const resizeEvent = createEvent('resize')
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const resizeEvent = createEvent('resize')
+          window.dispatchEvent(resizeEvent)
+          setTimeout(expectDone, 10)
+        })
 
-        window.dispatchEvent(resizeEvent)
-        setTimeout(expectDone, 10)
+        modal.show()
       })
-
-      modal.show()
     })
 
-    it('should not close modal when clicking on modal-content', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal">',
-        '  <div class="modal-dialog">',
-        '    <div class="modal-content"></div>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not close modal when clicking on modal-content', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal">',
+          '  <div class="modal-dialog">',
+          '    <div class="modal-content"></div>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
-
-      const shownCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toEqual(true)
-          done()
-        }, 10)
-      }
-
-      modalEl.addEventListener('shown.bs.modal', () => {
-        fixtureEl.querySelector('.modal-dialog').click()
-        fixtureEl.querySelector('.modal-content').click()
-        shownCallback()
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        throw new Error('Should not hide a modal')
-      })
+        const shownCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toEqual(true)
+            resolve()
+          }, 10)
+        }
 
-      modal.show()
-    })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          fixtureEl.querySelector('.modal-dialog').click()
+          fixtureEl.querySelector('.modal-content').click()
+          shownCallback()
+        })
 
-    it('should not close modal when clicking outside of modal-content if backdrop = false', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          throw new Error('Should not hide a modal')
+        })
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        backdrop: false
+        modal.show()
       })
+    })
 
-      const shownCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toBeTrue()
-          done()
-        }, 10)
-      }
+    it('should not close modal when clicking outside of modal-content if backdrop = false', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modalEl.click()
-        shownCallback()
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          backdrop: false
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        throw new Error('Should not hide a modal')
-      })
+        const shownCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toBeTrue()
+            resolve()
+          }, 10)
+        }
 
-      modal.show()
-    })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modalEl.click()
+          shownCallback()
+        })
 
-    it('should not close modal when clicking outside of modal-content if backdrop = static', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          throw new Error('Should not hide a modal')
+        })
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        backdrop: 'static'
+        modal.show()
       })
+    })
 
-      const shownCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toBeTrue()
-          done()
-        }, 10)
-      }
+    it('should not close modal when clicking outside of modal-content if backdrop = static', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modalEl.click()
-        shownCallback()
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          backdrop: 'static'
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        throw new Error('Should not hide a modal')
-      })
+        const shownCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toBeTrue()
+            resolve()
+          }, 10)
+        }
 
-      modal.show()
-    })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modalEl.click()
+          shownCallback()
+        })
 
-    it('should close modal when escape key is pressed with keyboard = true and backdrop is static', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          throw new Error('Should not hide a modal')
+        })
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        backdrop: 'static',
-        keyboard: true
+        modal.show()
       })
+    })
+    it('should close modal when escape key is pressed with keyboard = true and backdrop is static', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          backdrop: 'static',
+          keyboard: true
+        })
 
-      const shownCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toBeFalse()
-          done()
-        }, 10)
-      }
+        const shownCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toBeFalse()
+            resolve()
+          }, 10)
+        }
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const keydownEscape = createEvent('keydown')
-        keydownEscape.key = 'Escape'
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const keydownEscape = createEvent('keydown')
+          keydownEscape.key = 'Escape'
 
-        modalEl.dispatchEvent(keydownEscape)
-        shownCallback()
-      })
+          modalEl.dispatchEvent(keydownEscape)
+          shownCallback()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should not close modal when escape key is pressed with keyboard = false', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should not close modal when escape key is pressed with keyboard = false', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        keyboard: false
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          keyboard: false
+        })
 
-      const shownCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toBeTrue()
-          done()
-        }, 10)
-      }
+        const shownCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toBeTrue()
+            resolve()
+          }, 10)
+        }
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const keydownEscape = createEvent('keydown')
-        keydownEscape.key = 'Escape'
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const keydownEscape = createEvent('keydown')
+          keydownEscape.key = 'Escape'
 
-        modalEl.dispatchEvent(keydownEscape)
-        shownCallback()
-      })
+          modalEl.dispatchEvent(keydownEscape)
+          shownCallback()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        throw new Error('Should not hide a modal')
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          throw new Error('Should not hide a modal')
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should not overflow when clicking outside of modal-content if backdrop = static', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 20ms;"></div></div>'
+    it('should not overflow when clicking outside of modal-content if backdrop = static', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 20ms;"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl, {
-        backdrop: 'static'
-      })
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl, {
+          backdrop: 'static'
+        })
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modalEl.click()
-        setTimeout(() => {
-          expect(modalEl.clientHeight).toEqual(modalEl.scrollHeight)
-          done()
-        }, 20)
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modalEl.click()
+          setTimeout(() => {
+            expect(modalEl.clientHeight).toEqual(modalEl.scrollHeight)
+            resolve()
+          }, 20)
+        })
 
-      modal.show()
+        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>'
+    it('should not queue multiple callbacks when clicking outside of modal-content and backdrop = static', () => {
+      return new Promise(resolve => {
+        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'
-      })
+        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.addEventListener('shown.bs.modal', () => {
+          const spy = spyOn(modal, '_queueCallback').and.callThrough()
 
-        modalEl.click()
-        modalEl.click()
+          modalEl.click()
+          modalEl.click()
 
-        setTimeout(() => {
-          expect(spy).toHaveBeenCalledTimes(1)
-          done()
-        }, 20)
-      })
+          setTimeout(() => {
+            expect(spy).toHaveBeenCalledTimes(1)
+            resolve()
+          }, 20)
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should trap focus', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should trap focus', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      spyOn(modal._focustrap, 'activate').and.callThrough()
+        spyOn(modal._focustrap, 'activate').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modal._focustrap.activate).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modal._focustrap.activate).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
   })
 
   describe('hide', () => {
-    it('should hide a modal', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should hide a modal', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
-      const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
+        const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modal.hide()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modal.hide()
+        })
 
-      modalEl.addEventListener('hide.bs.modal', event => {
-        expect(event).toBeDefined()
-      })
+        modalEl.addEventListener('hide.bs.modal', event => {
+          expect(event).toBeDefined()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toBeNull()
-        expect(modalEl.getAttribute('role')).toBeNull()
-        expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
-        expect(modalEl.style.display).toEqual('none')
-        expect(backdropSpy).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toBeNull()
+          expect(modalEl.getAttribute('role')).toBeNull()
+          expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
+          expect(modalEl.style.display).toEqual('none')
+          expect(backdropSpy).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should close modal when clicking outside of modal-content', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should close modal when clicking outside of modal-content', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modalEl.click()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modalEl.click()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toBeNull()
-        expect(modalEl.getAttribute('role')).toBeNull()
-        expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
-        expect(modalEl.style.display).toEqual('none')
-        expect(document.querySelector('.modal-backdrop')).toBeNull()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toBeNull()
+          expect(modalEl.getAttribute('role')).toBeNull()
+          expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
+          expect(modalEl.style.display).toEqual('none')
+          expect(document.querySelector('.modal-backdrop')).toBeNull()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
     it('should do nothing is the modal is not shown', () => {
@@ -703,52 +751,56 @@ describe('Modal', () => {
       expect().nothing()
     })
 
-    it('should not hide a modal if hide is prevented', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should not hide a modal if hide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modal.hide()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modal.hide()
+        })
 
-      const hideCallback = () => {
-        setTimeout(() => {
-          expect(modal._isShown).toBeTrue()
-          done()
-        }, 10)
-      }
+        const hideCallback = () => {
+          setTimeout(() => {
+            expect(modal._isShown).toBeTrue()
+            resolve()
+          }, 10)
+        }
 
-      modalEl.addEventListener('hide.bs.modal', event => {
-        event.preventDefault()
-        hideCallback()
-      })
+        modalEl.addEventListener('hide.bs.modal', event => {
+          event.preventDefault()
+          hideCallback()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        throw new Error('should not trigger hidden')
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          throw new Error('should not trigger hidden')
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
 
-    it('should release focus trap', done => {
-      fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+    it('should release focus trap', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
-      spyOn(modal._focustrap, 'deactivate').and.callThrough()
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
+        spyOn(modal._focustrap, 'deactivate').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        modal.hide()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          modal.hide()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modal._focustrap.deactivate).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modal._focustrap.deactivate).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
   })
 
@@ -789,246 +841,260 @@ describe('Modal', () => {
   })
 
   describe('data-api', () => {
-    it('should toggle modal', done => {
-      fixtureEl.innerHTML = [
-        '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
-        '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
+    it('should toggle modal', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
+          '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+          expect(modalEl.getAttribute('role')).toEqual('dialog')
+          expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+          expect(modalEl.style.display).toEqual('block')
+          expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+          setTimeout(() => trigger.click(), 10)
+        })
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
-
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toEqual('true')
-        expect(modalEl.getAttribute('role')).toEqual('dialog')
-        expect(modalEl.getAttribute('aria-hidden')).toBeNull()
-        expect(modalEl.style.display).toEqual('block')
-        expect(document.querySelector('.modal-backdrop')).not.toBeNull()
-        setTimeout(() => trigger.click(), 10)
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toBeNull()
+          expect(modalEl.getAttribute('role')).toBeNull()
+          expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
+          expect(modalEl.style.display).toEqual('none')
+          expect(document.querySelector('.modal-backdrop')).toBeNull()
+          resolve()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toBeNull()
-        expect(modalEl.getAttribute('role')).toBeNull()
-        expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
-        expect(modalEl.style.display).toEqual('none')
-        expect(document.querySelector('.modal-backdrop')).toBeNull()
-        done()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should not recreate a new modal', done => {
-      fixtureEl.innerHTML = [
-        '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
-        '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
+    it('should not recreate a new modal', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
+          '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const modal = new Modal(modalEl)
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+        const modalEl = fixtureEl.querySelector('.modal')
+        const modal = new Modal(modalEl)
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
 
-      spyOn(modal, 'show').and.callThrough()
+        spyOn(modal, 'show').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modal.show).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modal.show).toHaveBeenCalled()
+          resolve()
+        })
 
-      trigger.click()
+        trigger.click()
+      })
     })
 
-    it('should prevent default when the trigger is <a> or <area>', done => {
-      fixtureEl.innerHTML = [
-        '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
-        '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
+    it('should prevent default when the trigger is <a> or <area>', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+          '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+
+        spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+          expect(modalEl.getAttribute('role')).toEqual('dialog')
+          expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+          expect(modalEl.style.display).toEqual('block')
+          expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+          expect(Event.prototype.preventDefault).toHaveBeenCalled()
+          resolve()
+        })
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
-
-      spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
-      modalEl.addEventListener('shown.bs.modal', () => {
-        expect(modalEl.getAttribute('aria-modal')).toEqual('true')
-        expect(modalEl.getAttribute('role')).toEqual('dialog')
-        expect(modalEl.getAttribute('aria-hidden')).toBeNull()
-        expect(modalEl.style.display).toEqual('block')
-        expect(document.querySelector('.modal-backdrop')).not.toBeNull()
-        expect(Event.prototype.preventDefault).toHaveBeenCalled()
-        done()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should focus the trigger on hide', done => {
-      fixtureEl.innerHTML = [
-        '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
-        '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
+    it('should focus the trigger on hide', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+          '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+        const modalEl = fixtureEl.querySelector('.modal')
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
 
-      spyOn(trigger, 'focus')
+        spyOn(trigger, 'focus')
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const modal = Modal.getInstance(modalEl)
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const modal = Modal.getInstance(modalEl)
 
-        modal.hide()
-      })
+          modal.hide()
+        })
 
-      const hideListener = () => {
-        setTimeout(() => {
-          expect(trigger.focus).toHaveBeenCalled()
-          done()
-        }, 20)
-      }
+        const hideListener = () => {
+          setTimeout(() => {
+            expect(trigger.focus).toHaveBeenCalled()
+            resolve()
+          }, 20)
+        }
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        hideListener()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          hideListener()
+        })
 
-      trigger.click()
+        trigger.click()
+      })
     })
+    it('should not prevent default when a click occurred on data-bs-dismiss="modal" where tagName is DIFFERENT than <a> or <area>', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal">',
+          '  <div class="modal-dialog">',
+          '    <button type="button" data-bs-dismiss="modal"></button>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const modalEl = fixtureEl.querySelector('.modal')
+        const btnClose = fixtureEl.querySelector('button[data-bs-dismiss="modal"]')
+        const modal = new Modal(modalEl)
+
+        spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+        modalEl.addEventListener('shown.bs.modal', () => {
+          btnClose.click()
+        })
 
-    it('should not prevent default when a click occurred on data-bs-dismiss="modal" where tagName is DIFFERENT than <a> or <area>', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal">',
-        '  <div class="modal-dialog">',
-        '    <button type="button" data-bs-dismiss="modal"></button>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const modalEl = fixtureEl.querySelector('.modal')
-      const btnClose = fixtureEl.querySelector('button[data-bs-dismiss="modal"]')
-      const modal = new Modal(modalEl)
-
-      spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
-      modalEl.addEventListener('shown.bs.modal', () => {
-        btnClose.click()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(Event.prototype.preventDefault).not.toHaveBeenCalled()
+          resolve()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(Event.prototype.preventDefault).not.toHaveBeenCalled()
-        done()
+        modal.show()
       })
-
-      modal.show()
     })
 
-    it('should prevent default when a click occurred on data-bs-dismiss="modal" where tagName is <a> or <area>', done => {
-      fixtureEl.innerHTML = [
-        '<div class="modal">',
-        '  <div class="modal-dialog">',
-        '    <a type="button" data-bs-dismiss="modal"></a>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should prevent default when a click occurred on data-bs-dismiss="modal" where tagName is <a> or <area>', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="modal">',
+          '  <div class="modal-dialog">',
+          '    <a type="button" data-bs-dismiss="modal"></a>',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const btnClose = fixtureEl.querySelector('a[data-bs-dismiss="modal"]')
-      const modal = new Modal(modalEl)
+        const modalEl = fixtureEl.querySelector('.modal')
+        const btnClose = fixtureEl.querySelector('a[data-bs-dismiss="modal"]')
+        const modal = new Modal(modalEl)
 
-      spyOn(Event.prototype, 'preventDefault').and.callThrough()
+        spyOn(Event.prototype, 'preventDefault').and.callThrough()
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        btnClose.click()
-      })
+        modalEl.addEventListener('shown.bs.modal', () => {
+          btnClose.click()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        expect(Event.prototype.preventDefault).toHaveBeenCalled()
-        done()
-      })
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          expect(Event.prototype.preventDefault).toHaveBeenCalled()
+          resolve()
+        })
 
-      modal.show()
+        modal.show()
+      })
     })
+    it('should not focus the trigger if the modal is not visible', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal" style="display: none;"></a>',
+          '<div id="exampleModal" class="modal" style="display: none;"><div class="modal-dialog"></div></div>'
+        ].join('')
 
-    it('should not focus the trigger if the modal is not visible', done => {
-      fixtureEl.innerHTML = [
-        '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal" style="display: none;"></a>',
-        '<div id="exampleModal" class="modal" style="display: none;"><div class="modal-dialog"></div></div>'
-      ].join('')
+        const modalEl = fixtureEl.querySelector('.modal')
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+        spyOn(trigger, 'focus')
 
-      spyOn(trigger, 'focus')
+        modalEl.addEventListener('shown.bs.modal', () => {
+          const modal = Modal.getInstance(modalEl)
 
-      modalEl.addEventListener('shown.bs.modal', () => {
-        const modal = Modal.getInstance(modalEl)
+          modal.hide()
+        })
 
-        modal.hide()
-      })
+        const hideListener = () => {
+          setTimeout(() => {
+            expect(trigger.focus).not.toHaveBeenCalled()
+            resolve()
+          }, 20)
+        }
 
-      const hideListener = () => {
-        setTimeout(() => {
-          expect(trigger.focus).not.toHaveBeenCalled()
-          done()
-        }, 20)
-      }
+        modalEl.addEventListener('hidden.bs.modal', () => {
+          hideListener()
+        })
 
-      modalEl.addEventListener('hidden.bs.modal', () => {
-        hideListener()
+        trigger.click()
       })
-
-      trigger.click()
     })
+    it('should not focus the trigger if the modal is not shown', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+          '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
 
-    it('should not focus the trigger if the modal is not shown', done => {
-      fixtureEl.innerHTML = [
-        '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
-        '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
+        const modalEl = fixtureEl.querySelector('.modal')
+        const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
 
-      const modalEl = fixtureEl.querySelector('.modal')
-      const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+        spyOn(trigger, 'focus')
 
-      spyOn(trigger, 'focus')
+        const showListener = () => {
+          setTimeout(() => {
+            expect(trigger.focus).not.toHaveBeenCalled()
+            resolve()
+          }, 10)
+        }
 
-      const showListener = () => {
-        setTimeout(() => {
-          expect(trigger.focus).not.toHaveBeenCalled()
-          done()
-        }, 10)
-      }
+        modalEl.addEventListener('show.bs.modal', event => {
+          event.preventDefault()
+          showListener()
+        })
 
-      modalEl.addEventListener('show.bs.modal', event => {
-        event.preventDefault()
-        showListener()
+        trigger.click()
       })
-
-      trigger.click()
     })
 
-    it('should call hide first, if another modal is open', done => {
-      fixtureEl.innerHTML = [
-        '<button data-bs-toggle="modal"  data-bs-target="#modal2"></button>',
-        '<div id="modal1" class="modal fade"><div class="modal-dialog"></div></div>',
-        '<div id="modal2" class="modal"><div class="modal-dialog"></div></div>'
-      ].join('')
-
-      const trigger2 = fixtureEl.querySelector('button')
-      const modalEl1 = document.querySelector('#modal1')
-      const modalEl2 = document.querySelector('#modal2')
-      const modal1 = new Modal(modalEl1)
-
-      modalEl1.addEventListener('shown.bs.modal', () => {
-        trigger2.click()
-      })
-      modalEl1.addEventListener('hidden.bs.modal', () => {
-        expect(Modal.getInstance(modalEl2)).not.toBeNull()
-        expect(modalEl2).toHaveClass('show')
-        done()
+    it('should call hide first, if another modal is open', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button data-bs-toggle="modal"  data-bs-target="#modal2"></button>',
+          '<div id="modal1" class="modal fade"><div class="modal-dialog"></div></div>',
+          '<div id="modal2" class="modal"><div class="modal-dialog"></div></div>'
+        ].join('')
+
+        const trigger2 = fixtureEl.querySelector('button')
+        const modalEl1 = document.querySelector('#modal1')
+        const modalEl2 = document.querySelector('#modal2')
+        const modal1 = new Modal(modalEl1)
+
+        modalEl1.addEventListener('shown.bs.modal', () => {
+          trigger2.click()
+        })
+        modalEl1.addEventListener('hidden.bs.modal', () => {
+          expect(Modal.getInstance(modalEl2)).not.toBeNull()
+          expect(modalEl2).toHaveClass('show')
+          resolve()
+        })
+        modal1.show()
       })
-      modal1.show()
     })
   })
-
   describe('jQueryInterface', () => {
     it('should create a modal', () => {
       fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
index 36ef45dcebb318dea29e74e950e37618f7f830c8..852ffa55604f5f531850dfa573beeb98f65e225a 100644 (file)
@@ -147,83 +147,91 @@ describe('Offcanvas', () => {
   })
 
   describe('options', () => {
-    it('if scroll is enabled, should allow body to scroll while offcanvas is open', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('if scroll is enabled, should allow body to scroll while offcanvas is open', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
-      spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl, { scroll: true })
+        spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+        spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl, { scroll: true })
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(ScrollBarHelper.prototype.hide).not.toHaveBeenCalled()
-        offCanvas.hide()
-      })
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(ScrollBarHelper.prototype.reset).not.toHaveBeenCalled()
-        done()
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(ScrollBarHelper.prototype.hide).not.toHaveBeenCalled()
+          offCanvas.hide()
+        })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(ScrollBarHelper.prototype.reset).not.toHaveBeenCalled()
+          resolve()
+        })
+        offCanvas.show()
       })
-      offCanvas.show()
     })
 
-    it('if scroll is disabled, should call ScrollBarHelper to handle scrollBar on body', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('if scroll is disabled, should call ScrollBarHelper to handle scrollBar on body', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
-      spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl, { scroll: false })
+        spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+        spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl, { scroll: false })
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
-        offCanvas.hide()
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
+          offCanvas.hide()
+        })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
+          resolve()
+        })
+        offCanvas.show()
       })
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
-        done()
-      })
-      offCanvas.show()
     })
 
-    it('should hide a shown element if user click on backdrop', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should hide a shown element if user click on backdrop', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl, { backdrop: true })
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl, { backdrop: true })
 
-      const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
-      spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
+        const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
+        spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvas._backdrop._config.clickCallback).toEqual(jasmine.any(Function))
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvas._backdrop._config.clickCallback).toEqual(jasmine.any(Function))
 
-        offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
-      })
+          offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
+        })
 
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(offCanvas._backdrop._config.clickCallback).toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(offCanvas._backdrop._config.clickCallback).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
 
-    it('should not trap focus if scroll is allowed', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should not trap focus if scroll is allowed', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl, {
-        scroll: true
-      })
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl, {
+          scroll: true
+        })
 
-      spyOn(offCanvas._focustrap, 'activate').and.callThrough()
+        spyOn(offCanvas._focustrap, 'activate').and.callThrough()
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvas._focustrap.activate).not.toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvas._focustrap.activate).not.toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
   })
 
@@ -241,44 +249,48 @@ describe('Offcanvas', () => {
       expect(offCanvas.show).toHaveBeenCalled()
     })
 
-    it('should call hide method if show class is present', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should call hide method if show class is present', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl)
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl)
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvasEl).toHaveClass('show')
-        spyOn(offCanvas, 'hide')
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvasEl).toHaveClass('show')
+          spyOn(offCanvas, 'hide')
 
-        offCanvas.toggle()
+          offCanvas.toggle()
 
-        expect(offCanvas.hide).toHaveBeenCalled()
-        done()
-      })
+          expect(offCanvas.hide).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
   })
 
   describe('show', () => {
-    it('should add `showing` class during opening and `show` class on end', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl)
+    it('should add `showing` class during opening and `show` class on end', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl)
 
-      offCanvasEl.addEventListener('show.bs.offcanvas', () => {
-        expect(offCanvasEl).not.toHaveClass('show')
-      })
+        offCanvasEl.addEventListener('show.bs.offcanvas', () => {
+          expect(offCanvasEl).not.toHaveClass('show')
+        })
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvasEl).not.toHaveClass('showing')
-        expect(offCanvasEl).toHaveClass('show')
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvasEl).not.toHaveClass('showing')
+          expect(offCanvasEl).toHaveClass('show')
+          resolve()
+        })
 
-      offCanvas.show()
-      expect(offCanvasEl).toHaveClass('showing')
+        offCanvas.show()
+        expect(offCanvasEl).toHaveClass('showing')
+      })
     })
 
     it('should do nothing if already shown', () => {
@@ -298,104 +310,114 @@ describe('Offcanvas', () => {
       expect(offCanvas._backdrop.show).not.toHaveBeenCalled()
     })
 
-    it('should show a hidden element', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should show a hidden element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl)
-      spyOn(offCanvas._backdrop, 'show').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl)
+        spyOn(offCanvas._backdrop, 'show').and.callThrough()
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvasEl).toHaveClass('show')
-        expect(offCanvas._backdrop.show).toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvasEl).toHaveClass('show')
+          expect(offCanvas._backdrop.show).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
 
-    it('should not fire shown when show is prevented', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should not fire shown when show is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl)
-      spyOn(offCanvas._backdrop, 'show').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl)
+        spyOn(offCanvas._backdrop, 'show').and.callThrough()
 
-      const expectEnd = () => {
-        setTimeout(() => {
-          expect(offCanvas._backdrop.show).not.toHaveBeenCalled()
-          done()
-        }, 10)
-      }
+        const expectEnd = () => {
+          setTimeout(() => {
+            expect(offCanvas._backdrop.show).not.toHaveBeenCalled()
+            resolve()
+          }, 10)
+        }
 
-      offCanvasEl.addEventListener('show.bs.offcanvas', event => {
-        event.preventDefault()
-        expectEnd()
-      })
+        offCanvasEl.addEventListener('show.bs.offcanvas', event => {
+          event.preventDefault()
+          expectEnd()
+        })
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        throw new Error('should not fire shown event')
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          throw new Error('should not fire shown event')
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
 
-    it('on window load, should make visible an offcanvas element, if its markup contains class "show"', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas show"></div>'
+    it('on window load, should make visible an offcanvas element, if its markup contains class "show"', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas show"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      spyOn(Offcanvas.prototype, 'show').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        spyOn(Offcanvas.prototype, 'show').and.callThrough()
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          resolve()
+        })
 
-      window.dispatchEvent(createEvent('load'))
+        window.dispatchEvent(createEvent('load'))
 
-      const instance = Offcanvas.getInstance(offCanvasEl)
-      expect(instance).not.toBeNull()
-      expect(Offcanvas.prototype.show).toHaveBeenCalled()
+        const instance = Offcanvas.getInstance(offCanvasEl)
+        expect(instance).not.toBeNull()
+        expect(Offcanvas.prototype.show).toHaveBeenCalled()
+      })
     })
 
-    it('should trap focus', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should trap focus', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl)
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl)
 
-      spyOn(offCanvas._focustrap, 'activate').and.callThrough()
+        spyOn(offCanvas._focustrap, 'activate').and.callThrough()
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvas._focustrap.activate).toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvas._focustrap.activate).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.show()
+        offCanvas.show()
+      })
     })
   })
 
   describe('hide', () => {
-    it('should add `hiding` class during closing and remover `show` & `hiding` classes on end', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
-      const offCanvasEl = fixtureEl.querySelector('.offcanvas')
-      const offCanvas = new Offcanvas(offCanvasEl)
+    it('should add `hiding` class during closing and remover `show` & `hiding` classes on end', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+        const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+        const offCanvas = new Offcanvas(offCanvasEl)
 
-      offCanvasEl.addEventListener('hide.bs.offcanvas', () => {
-        expect(offCanvasEl).not.toHaveClass('showing')
-        expect(offCanvasEl).toHaveClass('show')
-      })
+        offCanvasEl.addEventListener('hide.bs.offcanvas', () => {
+          expect(offCanvasEl).not.toHaveClass('showing')
+          expect(offCanvasEl).toHaveClass('show')
+        })
 
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(offCanvasEl).not.toHaveClass('hiding')
-        expect(offCanvasEl).not.toHaveClass('show')
-        done()
-      })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(offCanvasEl).not.toHaveClass('hiding')
+          expect(offCanvasEl).not.toHaveClass('show')
+          resolve()
+        })
 
-      offCanvas.show()
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        offCanvas.hide()
-        expect(offCanvasEl).not.toHaveClass('showing')
-        expect(offCanvasEl).toHaveClass('hiding')
+        offCanvas.show()
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          offCanvas.hide()
+          expect(offCanvasEl).not.toHaveClass('showing')
+          expect(offCanvasEl).toHaveClass('hiding')
+        })
       })
     })
 
@@ -413,65 +435,71 @@ describe('Offcanvas', () => {
       expect(EventHandler.trigger).not.toHaveBeenCalled()
     })
 
-    it('should hide a shown element', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should hide a shown element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl)
-      spyOn(offCanvas._backdrop, 'hide').and.callThrough()
-      offCanvas.show()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl)
+        spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+        offCanvas.show()
 
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(offCanvasEl).not.toHaveClass('show')
-        expect(offCanvas._backdrop.hide).toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(offCanvasEl).not.toHaveClass('show')
+          expect(offCanvas._backdrop.hide).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.hide()
+        offCanvas.hide()
+      })
     })
 
-    it('should not fire hidden when hide is prevented', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should not fire hidden when hide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl)
-      spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl)
+        spyOn(offCanvas._backdrop, 'hide').and.callThrough()
 
-      offCanvas.show()
+        offCanvas.show()
 
-      const expectEnd = () => {
-        setTimeout(() => {
-          expect(offCanvas._backdrop.hide).not.toHaveBeenCalled()
-          done()
-        }, 10)
-      }
+        const expectEnd = () => {
+          setTimeout(() => {
+            expect(offCanvas._backdrop.hide).not.toHaveBeenCalled()
+            resolve()
+          }, 10)
+        }
 
-      offCanvasEl.addEventListener('hide.bs.offcanvas', event => {
-        event.preventDefault()
-        expectEnd()
-      })
+        offCanvasEl.addEventListener('hide.bs.offcanvas', event => {
+          event.preventDefault()
+          expectEnd()
+        })
 
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        throw new Error('should not fire hidden event')
-      })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          throw new Error('should not fire hidden event')
+        })
 
-      offCanvas.hide()
+        offCanvas.hide()
+      })
     })
 
-    it('should release focus trap', done => {
-      fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+    it('should release focus trap', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="offcanvas"></div>'
 
-      const offCanvasEl = fixtureEl.querySelector('div')
-      const offCanvas = new Offcanvas(offCanvasEl)
-      spyOn(offCanvas._focustrap, 'deactivate').and.callThrough()
-      offCanvas.show()
+        const offCanvasEl = fixtureEl.querySelector('div')
+        const offCanvas = new Offcanvas(offCanvasEl)
+        spyOn(offCanvas._focustrap, 'deactivate').and.callThrough()
+        offCanvas.show()
 
-      offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        expect(offCanvas._focustrap.deactivate).toHaveBeenCalled()
-        done()
-      })
+        offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          expect(offCanvas._focustrap.deactivate).toHaveBeenCalled()
+          resolve()
+        })
 
-      offCanvas.hide()
+        offCanvas.hide()
+      })
     })
   })
 
@@ -501,22 +529,24 @@ describe('Offcanvas', () => {
   })
 
   describe('data-api', () => {
-    it('should not prevent event for input', done => {
-      fixtureEl.innerHTML = [
-        '<input type="checkbox" data-bs-toggle="offcanvas" data-bs-target="#offcanvasdiv1" />',
-        '<div id="offcanvasdiv1" class="offcanvas"></div>'
-      ].join('')
+    it('should not prevent event for input', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<input type="checkbox" data-bs-toggle="offcanvas" data-bs-target="#offcanvasdiv1" />',
+          '<div id="offcanvasdiv1" class="offcanvas"></div>'
+        ].join('')
 
-      const target = fixtureEl.querySelector('input')
-      const offCanvasEl = fixtureEl.querySelector('#offcanvasdiv1')
+        const target = fixtureEl.querySelector('input')
+        const offCanvasEl = fixtureEl.querySelector('#offcanvasdiv1')
 
-      offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        expect(offCanvasEl).toHaveClass('show')
-        expect(target.checked).toBeTrue()
-        done()
-      })
+        offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          expect(offCanvasEl).toHaveClass('show')
+          expect(target.checked).toBeTrue()
+          resolve()
+        })
 
-      target.click()
+        target.click()
+      })
     })
 
     it('should not call toggle on disabled elements', () => {
@@ -534,76 +564,82 @@ describe('Offcanvas', () => {
       expect(Offcanvas.prototype.toggle).not.toHaveBeenCalled()
     })
 
-    it('should call hide first, if another offcanvas is open', done => {
-      fixtureEl.innerHTML = [
-        '<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2"></button>',
-        '<div id="offcanvas1" class="offcanvas"></div>',
-        '<div id="offcanvas2" class="offcanvas"></div>'
-      ].join('')
-
-      const trigger2 = fixtureEl.querySelector('#btn2')
-      const offcanvasEl1 = document.querySelector('#offcanvas1')
-      const offcanvasEl2 = document.querySelector('#offcanvas2')
-      const offcanvas1 = new Offcanvas(offcanvasEl1)
-
-      offcanvasEl1.addEventListener('shown.bs.offcanvas', () => {
-        trigger2.click()
+    it('should call hide first, if another offcanvas is open', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2"></button>',
+          '<div id="offcanvas1" class="offcanvas"></div>',
+          '<div id="offcanvas2" class="offcanvas"></div>'
+        ].join('')
+
+        const trigger2 = fixtureEl.querySelector('#btn2')
+        const offcanvasEl1 = document.querySelector('#offcanvas1')
+        const offcanvasEl2 = document.querySelector('#offcanvas2')
+        const offcanvas1 = new Offcanvas(offcanvasEl1)
+
+        offcanvasEl1.addEventListener('shown.bs.offcanvas', () => {
+          trigger2.click()
+        })
+        offcanvasEl1.addEventListener('hidden.bs.offcanvas', () => {
+          expect(Offcanvas.getInstance(offcanvasEl2)).not.toBeNull()
+          resolve()
+        })
+        offcanvas1.show()
+      })
+    })
+
+    it('should focus on trigger element after closing offcanvas', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
+          '<div id="offcanvas" class="offcanvas"></div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('#btn')
+        const offcanvasEl = fixtureEl.querySelector('#offcanvas')
+        const offcanvas = new Offcanvas(offcanvasEl)
+        spyOn(trigger, 'focus')
+
+        offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          offcanvas.hide()
+        })
+        offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          setTimeout(() => {
+            expect(trigger.focus).toHaveBeenCalled()
+            resolve()
+          }, 5)
+        })
+
+        trigger.click()
+      })
+    })
+
+    it('should not focus on trigger element after closing offcanvas, if it is not visible', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
+          '<div id="offcanvas" class="offcanvas"></div>'
+        ].join('')
+
+        const trigger = fixtureEl.querySelector('#btn')
+        const offcanvasEl = fixtureEl.querySelector('#offcanvas')
+        const offcanvas = new Offcanvas(offcanvasEl)
+        spyOn(trigger, 'focus')
+
+        offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
+          trigger.style.display = 'none'
+          offcanvas.hide()
+        })
+        offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+          setTimeout(() => {
+            expect(isVisible(trigger)).toBeFalse()
+            expect(trigger.focus).not.toHaveBeenCalled()
+            resolve()
+          }, 5)
+        })
+
+        trigger.click()
       })
-      offcanvasEl1.addEventListener('hidden.bs.offcanvas', () => {
-        expect(Offcanvas.getInstance(offcanvasEl2)).not.toBeNull()
-        done()
-      })
-      offcanvas1.show()
-    })
-
-    it('should focus on trigger element after closing offcanvas', done => {
-      fixtureEl.innerHTML = [
-        '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
-        '<div id="offcanvas" class="offcanvas"></div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('#btn')
-      const offcanvasEl = fixtureEl.querySelector('#offcanvas')
-      const offcanvas = new Offcanvas(offcanvasEl)
-      spyOn(trigger, 'focus')
-
-      offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        offcanvas.hide()
-      })
-      offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        setTimeout(() => {
-          expect(trigger.focus).toHaveBeenCalled()
-          done()
-        }, 5)
-      })
-
-      trigger.click()
-    })
-
-    it('should not focus on trigger element after closing offcanvas, if it is not visible', done => {
-      fixtureEl.innerHTML = [
-        '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
-        '<div id="offcanvas" class="offcanvas"></div>'
-      ].join('')
-
-      const trigger = fixtureEl.querySelector('#btn')
-      const offcanvasEl = fixtureEl.querySelector('#offcanvas')
-      const offcanvas = new Offcanvas(offcanvasEl)
-      spyOn(trigger, 'focus')
-
-      offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
-        trigger.style.display = 'none'
-        offcanvas.hide()
-      })
-      offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
-        setTimeout(() => {
-          expect(isVisible(trigger)).toBeFalse()
-          expect(trigger.focus).not.toHaveBeenCalled()
-          done()
-        }, 5)
-      })
-
-      trigger.click()
     })
   })
 
index a04bd21c60e5da3b879b0bebc087603974c2cf5f..7bbd52b1dba856e5151e71472150f5baa2f9934b 100644 (file)
@@ -62,113 +62,125 @@ describe('Popover', () => {
   })
 
   describe('show', () => {
-    it('should show a popover', done => {
-      fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
+    it('should show a popover', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl)
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl)
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        expect(document.querySelector('.popover')).not.toBeNull()
-        done()
-      })
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          expect(document.querySelector('.popover')).not.toBeNull()
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
-    it('should set title and content from functions', done => {
-      fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+    it('should set title and content from functions', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl, {
-        title: () => 'Bootstrap',
-        content: () => 'loves writing tests (╯°□°)╯︵ ┻━┻'
-      })
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          title: () => 'Bootstrap',
+          content: () => 'loves writing tests (╯°□°)╯︵ ┻━┻'
+        })
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        const popoverDisplayed = document.querySelector('.popover')
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
 
-        expect(popoverDisplayed).not.toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Bootstrap')
-        expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('loves writing tests (╯°□°)╯︵ ┻━┻')
-        done()
-      })
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Bootstrap')
+          expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('loves writing tests (╯°□°)╯︵ ┻━┻')
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
-    it('should show a popover with just content without having header', done => {
-      fixtureEl.innerHTML = '<a href="#">Nice link</a>'
+    it('should show a popover with just content without having header', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#">Nice link</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl, {
-        content: 'Some beautiful content :)'
-      })
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          content: 'Some beautiful content :)'
+        })
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        const popoverDisplayed = document.querySelector('.popover')
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
 
-        expect(popoverDisplayed).not.toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-header')).toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Some beautiful content :)')
-        done()
-      })
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header')).toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Some beautiful content :)')
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
-    it('should show a popover with just title without having body', done => {
-      fixtureEl.innerHTML = '<a href="#">Nice link</a>'
+    it('should show a popover with just title without having body', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#">Nice link</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl, {
-        title: 'Title which does not require content'
-      })
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          title: 'Title which does not require content'
+        })
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        const popoverDisplayed = document.querySelector('.popover')
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
 
-        expect(popoverDisplayed).not.toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
-        done()
-      })
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
-    it('should show a popover with just title without having body using data-attribute to get config', done => {
-      fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="Title which does not require content">Nice link</a>'
+    it('should show a popover with just title without having body using data-attribute to get config', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="Title which does not require content">Nice link</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl)
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl)
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        const popoverDisplayed = document.querySelector('.popover')
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
 
-        expect(popoverDisplayed).not.toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
-        done()
-      })
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
-    it('should NOT show a popover without `title` and `content`', done => {
-      fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="">Nice link</a>'
+    it('should NOT show a popover without `title` and `content`', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="">Nice link</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl, { animation: false })
-      spyOn(EventHandler, 'trigger').and.callThrough()
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, { animation: false })
+        spyOn(EventHandler, 'trigger').and.callThrough()
 
-      setTimeout(() => {
-        expect(EventHandler.trigger).not.toHaveBeenCalled()
-        expect(document.querySelector('.popover')).toBeNull()
-        done()
-      })
+        setTimeout(() => {
+          expect(EventHandler.trigger).not.toHaveBeenCalled()
+          expect(document.querySelector('.popover')).toBeNull()
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
 
     it('"setContent" should keep the initial template', () => {
@@ -187,72 +199,78 @@ describe('Popover', () => {
       expect(tip.querySelector('.popover-body')).not.toBeNull()
     })
 
-    it('should call setContent once', done => {
-      fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
-
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl, {
-        content: 'Popover content'
-      })
-      expect(popover._templateFactory).toBeNull()
-      let spy = null
-      let times = 1
-
-      popoverEl.addEventListener('hidden.bs.popover', () => {
+    it('should call setContent once', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          content: 'Popover content'
+        })
+        expect(popover._templateFactory).toBeNull()
+        let spy = null
+        let times = 1
+
+        popoverEl.addEventListener('hidden.bs.popover', () => {
+          popover.show()
+        })
+
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
+          const popoverDisplayed = document.querySelector('.popover')
+
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
+          expect(spy).toHaveBeenCalledTimes(0)
+          if (times > 1) {
+            resolve()
+          }
+
+          times++
+          popover.hide()
+        })
         popover.show()
       })
-
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
-        const popoverDisplayed = document.querySelector('.popover')
-
-        expect(popoverDisplayed).not.toBeNull()
-        expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
-        expect(spy).toHaveBeenCalledTimes(0)
-        if (times > 1) {
-          done()
-        }
-
-        times++
-        popover.hide()
-      })
-      popover.show()
     })
 
-    it('should show a popover with provided custom class', done => {
-      fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap" data-bs-custom-class="custom-class">BS twitter</a>'
+    it('should show a popover with provided custom class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap" data-bs-custom-class="custom-class">BS twitter</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl)
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl)
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        const tip = document.querySelector('.popover')
-        expect(tip).not.toBeNull()
-        expect(tip).toHaveClass('custom-class')
-        done()
-      })
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const tip = document.querySelector('.popover')
+          expect(tip).not.toBeNull()
+          expect(tip).toHaveClass('custom-class')
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
   })
 
   describe('hide', () => {
-    it('should hide a popover', done => {
-      fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
+    it('should hide a popover', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
 
-      const popoverEl = fixtureEl.querySelector('a')
-      const popover = new Popover(popoverEl)
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl)
 
-      popoverEl.addEventListener('shown.bs.popover', () => {
-        popover.hide()
-      })
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          popover.hide()
+        })
 
-      popoverEl.addEventListener('hidden.bs.popover', () => {
-        expect(document.querySelector('.popover')).toBeNull()
-        done()
-      })
+        popoverEl.addEventListener('hidden.bs.popover', () => {
+          expect(document.querySelector('.popover')).toBeNull()
+          resolve()
+        })
 
-      popover.show()
+        popover.show()
+      })
     })
   })
 
index 5c044e697aecd0dc7e6fe7968438eacb2e16e263..291654d9beffe59847db50b064f2a3700e813185 100644 (file)
@@ -1,6 +1,6 @@
 import ScrollSpy from '../../src/scrollspy'
 import Manipulator from '../../src/dom/manipulator'
-import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
+import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
 
 describe('ScrollSpy', () => {
   let fixtureEl
@@ -85,418 +85,436 @@ describe('ScrollSpy', () => {
       expect(scrollSpy._targets).toHaveSize(2)
     })
 
-    it('should only switch "active" class on current target', done => {
-      fixtureEl.innerHTML = [
-        '<div id="root" class="active" style="display: block">',
-        '  <div class="topbar">',
-        '    <div class="topbar-inner">',
-        '      <div class="container" id="ss-target">',
-        '        <ul class="nav">',
-        '          <li class="nav-item"><a href="#masthead">Overview</a></li>',
-        '          <li class="nav-item"><a href="#detail">Detail</a></li>',
-        '        </ul>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '  <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
-        '    <div style="height: 200px;">',
-        '      <h4 id="masthead">Overview</h4>',
-        '      <p style="height: 200px;"></p>',
-        '    </div>',
-        '    <div style="height: 200px;">',
-        '      <h4 id="detail">Detail</h4>',
-        '      <p style="height: 200px;"></p>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
-      const rootEl = fixtureEl.querySelector('#root')
-      const scrollSpy = new ScrollSpy(scrollSpyEl, {
-        target: 'ss-target'
-      })
-
-      spyOn(scrollSpy, '_process').and.callThrough()
-
-      scrollSpyEl.addEventListener('scroll', () => {
-        expect(rootEl).toHaveClass('active')
-        expect(scrollSpy._process).toHaveBeenCalled()
-        done()
+    it('should only switch "active" class on current target', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="root" class="active" style="display: block">',
+          '  <div class="topbar">',
+          '    <div class="topbar-inner">',
+          '      <div class="container" id="ss-target">',
+          '        <ul class="nav">',
+          '          <li class="nav-item"><a href="#masthead">Overview</a></li>',
+          '          <li class="nav-item"><a href="#detail">Detail</a></li>',
+          '        </ul>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '  <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
+          '    <div style="height: 200px;">',
+          '      <h4 id="masthead">Overview</h4>',
+          '      <p style="height: 200px;"></p>',
+          '    </div>',
+          '    <div style="height: 200px;">',
+          '      <h4 id="detail">Detail</h4>',
+          '      <p style="height: 200px;"></p>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
+        const rootEl = fixtureEl.querySelector('#root')
+        const scrollSpy = new ScrollSpy(scrollSpyEl, {
+          target: 'ss-target'
+        })
+
+        spyOn(scrollSpy, '_process').and.callThrough()
+
+        scrollSpyEl.addEventListener('scroll', () => {
+          expect(rootEl).toHaveClass('active')
+          expect(scrollSpy._process).toHaveBeenCalled()
+          resolve()
+        })
+
+        scrollSpyEl.scrollTop = 350
       })
-
-      scrollSpyEl.scrollTop = 350
     })
 
-    it('should only switch "active" class on current target specified w element', done => {
-      fixtureEl.innerHTML = [
-        '<div id="root" class="active" style="display: block">',
-        '  <div class="topbar">',
-        '    <div class="topbar-inner">',
-        '      <div class="container" id="ss-target">',
-        '        <ul class="nav">',
-        '          <li class="nav-item"><a href="#masthead">Overview</a></li>',
-        '          <li class="nav-item"><a href="#detail">Detail</a></li>',
-        '        </ul>',
-        '      </div>',
-        '    </div>',
-        '  </div>',
-        '  <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
-        '    <div style="height: 200px;">',
-        '      <h4 id="masthead">Overview</h4>',
-        '      <p style="height: 200px;"></p>',
-        '    </div>',
-        '    <div style="height: 200px;">',
-        '      <h4 id="detail">Detail</h4>',
-        '      <p style="height: 200px;"></p>',
-        '    </div>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
-      const rootEl = fixtureEl.querySelector('#root')
-      const scrollSpy = new ScrollSpy(scrollSpyEl, {
-        target: fixtureEl.querySelector('#ss-target')
-      })
-
-      spyOn(scrollSpy, '_process').and.callThrough()
-
-      scrollSpyEl.addEventListener('scroll', () => {
-        expect(rootEl).toHaveClass('active')
-        expect(scrollSpy._process).toHaveBeenCalled()
-        done()
+    it('should only switch "active" class on current target specified w element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="root" class="active" style="display: block">',
+          '  <div class="topbar">',
+          '    <div class="topbar-inner">',
+          '      <div class="container" id="ss-target">',
+          '        <ul class="nav">',
+          '          <li class="nav-item"><a href="#masthead">Overview</a></li>',
+          '          <li class="nav-item"><a href="#detail">Detail</a></li>',
+          '        </ul>',
+          '      </div>',
+          '    </div>',
+          '  </div>',
+          '  <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
+          '    <div style="height: 200px;">',
+          '      <h4 id="masthead">Overview</h4>',
+          '      <p style="height: 200px;"></p>',
+          '    </div>',
+          '    <div style="height: 200px;">',
+          '      <h4 id="detail">Detail</h4>',
+          '      <p style="height: 200px;"></p>',
+          '    </div>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
+        const rootEl = fixtureEl.querySelector('#root')
+        const scrollSpy = new ScrollSpy(scrollSpyEl, {
+          target: fixtureEl.querySelector('#ss-target')
+        })
+
+        spyOn(scrollSpy, '_process').and.callThrough()
+
+        scrollSpyEl.addEventListener('scroll', () => {
+          expect(rootEl).toHaveClass('active')
+          expect(scrollSpy._process).toHaveBeenCalled()
+          resolve()
+        })
+
+        scrollSpyEl.scrollTop = 350
       })
-
-      scrollSpyEl.scrollTop = 350
     })
 
-    it('should correctly select middle navigation option when large offset is used', done => {
-      fixtureEl.innerHTML = [
-        '<div id="header" style="height: 500px;"></div>',
-        '<nav id="navigation" class="navbar">',
-        '  <ul class="navbar-nav">',
-        '    <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
-        '    <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
-        '    <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
-        '  </ul>',
-        '</nav>',
-        '<div id="content" style="height: 200px; overflow-y: auto;">',
-        '  <div id="one" style="height: 500px;"></div>',
-        '  <div id="two" style="height: 300px;"></div>',
-        '  <div id="three" style="height: 10px;"></div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('#content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        target: '#navigation',
-        offset: Manipulator.position(contentEl).top
-      })
-
-      spyOn(scrollSpy, '_process').and.callThrough()
-
-      contentEl.addEventListener('scroll', () => {
-        expect(fixtureEl.querySelector('#one-link')).not.toHaveClass('active')
-        expect(fixtureEl.querySelector('#two-link')).toHaveClass('active')
-        expect(fixtureEl.querySelector('#three-link')).not.toHaveClass('active')
-        expect(scrollSpy._process).toHaveBeenCalled()
-        done()
+    it('should correctly select middle navigation option when large offset is used', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="header" style="height: 500px;"></div>',
+          '<nav id="navigation" class="navbar">',
+          '  <ul class="navbar-nav">',
+          '    <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
+          '    <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
+          '    <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
+          '  </ul>',
+          '</nav>',
+          '<div id="content" style="height: 200px; overflow-y: auto;">',
+          '  <div id="one" style="height: 500px;"></div>',
+          '  <div id="two" style="height: 300px;"></div>',
+          '  <div id="three" style="height: 10px;"></div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('#content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          target: '#navigation',
+          offset: Manipulator.position(contentEl).top
+        })
+
+        spyOn(scrollSpy, '_process').and.callThrough()
+
+        contentEl.addEventListener('scroll', () => {
+          expect(fixtureEl.querySelector('#one-link')).not.toHaveClass('active')
+          expect(fixtureEl.querySelector('#two-link')).toHaveClass('active')
+          expect(fixtureEl.querySelector('#three-link')).not.toHaveClass('active')
+          expect(scrollSpy._process).toHaveBeenCalled()
+          resolve()
+        })
+
+        contentEl.scrollTop = 550
       })
-
-      contentEl.scrollTop = 550
     })
 
-    it('should add the active class to the correct element', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar">',
-        '  <ul class="nav">',
-        '    <li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>',
-        '    <li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>',
-        '  </ul>',
-        '</nav>',
-        '<div class="content" style="overflow: auto; height: 50px">',
-        '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
-        '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('.content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        offset: 0,
-        target: '.navbar'
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      testElementIsActiveAfterScroll({
-        elementSelector: '#a-1',
-        targetSelector: '#div-1',
-        contentEl,
-        scrollSpy,
-        spy,
-        cb: () => {
-          testElementIsActiveAfterScroll({
-            elementSelector: '#a-2',
-            targetSelector: '#div-2',
-            contentEl,
-            scrollSpy,
-            spy,
-            cb: () => done()
-          })
-        }
+    it('should add the active class to the correct element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar">',
+          '  <ul class="nav">',
+          '    <li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>',
+          '    <li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>',
+          '  </ul>',
+          '</nav>',
+          '<div class="content" style="overflow: auto; height: 50px">',
+          '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+          '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('.content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          offset: 0,
+          target: '.navbar'
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        testElementIsActiveAfterScroll({
+          elementSelector: '#a-1',
+          targetSelector: '#div-1',
+          contentEl,
+          scrollSpy,
+          spy,
+          cb: () => {
+            testElementIsActiveAfterScroll({
+              elementSelector: '#a-2',
+              targetSelector: '#div-2',
+              contentEl,
+              scrollSpy,
+              spy,
+              cb: resolve
+            })
+          }
+        })
       })
     })
 
-    it('should add the active class to the correct element (nav markup)', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar">',
-        '  <nav class="nav">',
-        '    <a class="nav-link" id="a-1" href="#div-1">div 1</a>',
-        '    <a class="nav-link" id="a-2" href="#div-2">div 2</a>',
-        '  </nav>',
-        '</nav>',
-        '<div class="content" style="overflow: auto; height: 50px">',
-        '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
-        '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('.content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        offset: 0,
-        target: '.navbar'
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      testElementIsActiveAfterScroll({
-        elementSelector: '#a-1',
-        targetSelector: '#div-1',
-        contentEl,
-        scrollSpy,
-        spy,
-        cb: () => {
-          testElementIsActiveAfterScroll({
-            elementSelector: '#a-2',
-            targetSelector: '#div-2',
-            contentEl,
-            scrollSpy,
-            spy,
-            cb: () => done()
-          })
-        }
+    it('should add the active class to the correct element (nav markup)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar">',
+          '  <nav class="nav">',
+          '    <a class="nav-link" id="a-1" href="#div-1">div 1</a>',
+          '    <a class="nav-link" id="a-2" href="#div-2">div 2</a>',
+          '  </nav>',
+          '</nav>',
+          '<div class="content" style="overflow: auto; height: 50px">',
+          '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+          '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('.content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          offset: 0,
+          target: '.navbar'
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        testElementIsActiveAfterScroll({
+          elementSelector: '#a-1',
+          targetSelector: '#div-1',
+          contentEl,
+          scrollSpy,
+          spy,
+          cb: () => {
+            testElementIsActiveAfterScroll({
+              elementSelector: '#a-2',
+              targetSelector: '#div-2',
+              contentEl,
+              scrollSpy,
+              spy,
+              cb: resolve
+            })
+          }
+        })
       })
     })
 
-    it('should add the active class to the correct element (list-group markup)', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar">',
-        '  <div class="list-group">',
-        '    <a class="list-group-item" id="a-1" href="#div-1">div 1</a>',
-        '    <a class="list-group-item" id="a-2" href="#div-2">div 2</a>',
-        '  </div>',
-        '</nav>',
-        '<div class="content" style="overflow: auto; height: 50px">',
-        '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
-        '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('.content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        offset: 0,
-        target: '.navbar'
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      testElementIsActiveAfterScroll({
-        elementSelector: '#a-1',
-        targetSelector: '#div-1',
-        contentEl,
-        scrollSpy,
-        spy,
-        cb: () => {
-          testElementIsActiveAfterScroll({
-            elementSelector: '#a-2',
-            targetSelector: '#div-2',
-            contentEl,
-            scrollSpy,
-            spy,
-            cb: () => done()
-          })
-        }
+    it('should add the active class to the correct element (list-group markup)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar">',
+          '  <div class="list-group">',
+          '    <a class="list-group-item" id="a-1" href="#div-1">div 1</a>',
+          '    <a class="list-group-item" id="a-2" href="#div-2">div 2</a>',
+          '  </div>',
+          '</nav>',
+          '<div class="content" style="overflow: auto; height: 50px">',
+          '  <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+          '  <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('.content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          offset: 0,
+          target: '.navbar'
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        testElementIsActiveAfterScroll({
+          elementSelector: '#a-1',
+          targetSelector: '#div-1',
+          contentEl,
+          scrollSpy,
+          spy,
+          cb: () => {
+            testElementIsActiveAfterScroll({
+              elementSelector: '#a-2',
+              targetSelector: '#div-2',
+              contentEl,
+              scrollSpy,
+              spy,
+              cb: resolve
+            })
+          }
+        })
       })
     })
 
-    it('should clear selection if above the first section', done => {
-      fixtureEl.innerHTML = [
-        '<div id="header" style="height: 500px;"></div>',
-        '<nav id="navigation" class="navbar">',
-        '  <ul class="navbar-nav">',
-        '    <li class="nav-item"><a id="one-link"   class="nav-link active" href="#one">One</a></li>',
-        '    <li class="nav-item"><a id="two-link"   class="nav-link" href="#two">Two</a></li>',
-        '    <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
-        '  </ul>',
-        '</nav>',
-        '<div id="content" style="height: 200px; overflow-y: auto;">',
-        '  <div id="spacer" style="height: 100px;"></div>',
-        '  <div id="one" style="height: 100px;"></div>',
-        '  <div id="two" style="height: 100px;"></div>',
-        '  <div id="three" style="height: 100px;"></div>',
-        '  <div id="spacer" style="height: 100px;"></div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('#content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        target: '#navigation',
-        offset: Manipulator.position(contentEl).top
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      let firstTime = true
-
-      contentEl.addEventListener('scroll', () => {
-        const active = fixtureEl.querySelector('.active')
-
-        expect(spy).toHaveBeenCalled()
-        spy.calls.reset()
-        if (firstTime) {
-          expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
-          expect(active.getAttribute('id')).toEqual('two-link')
-          firstTime = false
-          contentEl.scrollTop = 0
-        } else {
-          expect(active).toBeNull()
-          done()
-        }
+    it('should clear selection if above the first section', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="header" style="height: 500px;"></div>',
+          '<nav id="navigation" class="navbar">',
+          '  <ul class="navbar-nav">',
+          '    <li class="nav-item"><a id="one-link"   class="nav-link active" href="#one">One</a></li>',
+          '    <li class="nav-item"><a id="two-link"   class="nav-link" href="#two">Two</a></li>',
+          '    <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
+          '  </ul>',
+          '</nav>',
+          '<div id="content" style="height: 200px; overflow-y: auto;">',
+          '  <div id="spacer" style="height: 100px;"></div>',
+          '  <div id="one" style="height: 100px;"></div>',
+          '  <div id="two" style="height: 100px;"></div>',
+          '  <div id="three" style="height: 100px;"></div>',
+          '  <div id="spacer" style="height: 100px;"></div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('#content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          target: '#navigation',
+          offset: Manipulator.position(contentEl).top
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        let firstTime = true
+
+        contentEl.addEventListener('scroll', () => {
+          const active = fixtureEl.querySelector('.active')
+
+          expect(spy).toHaveBeenCalled()
+          spy.calls.reset()
+          if (firstTime) {
+            expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+            expect(active.getAttribute('id')).toEqual('two-link')
+            firstTime = false
+            contentEl.scrollTop = 0
+          } else {
+            expect(active).toBeNull()
+            resolve()
+          }
+        })
+
+        contentEl.scrollTop = 201
       })
-
-      contentEl.scrollTop = 201
     })
 
-    it('should not clear selection if above the first section and first section is at the top', done => {
-      fixtureEl.innerHTML = [
-        '<div id="header" style="height: 500px;"></div>',
-        '<nav id="navigation" class="navbar">',
-        '  <ul class="navbar-nav">',
-        '    <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
-        '    <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
-        '    <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
-        '  </ul>',
-        '</nav>',
-        '<div id="content" style="height: 200px; overflow-y: auto;">',
-        '  <div id="one" style="height: 100px;"></div>',
-        '  <div id="two" style="height: 100px;"></div>',
-        '  <div id="three" style="height: 100px;"></div>',
-        '  <div id="spacer" style="height: 100px;"></div>',
-        '</div>'
-      ].join('')
-
-      const negativeHeight = -10
-      const startOfSectionTwo = 101
-      const contentEl = fixtureEl.querySelector('#content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        target: '#navigation',
-        offset: contentEl.offsetTop
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      let firstTime = true
-
-      contentEl.addEventListener('scroll', () => {
-        const active = fixtureEl.querySelector('.active')
-
-        expect(spy).toHaveBeenCalled()
-        spy.calls.reset()
-        if (firstTime) {
-          expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
-          expect(active.getAttribute('id')).toEqual('two-link')
-          firstTime = false
-          contentEl.scrollTop = negativeHeight
-        } else {
-          expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
-          expect(active.getAttribute('id')).toEqual('one-link')
-          done()
-        }
+    it('should not clear selection if above the first section and first section is at the top', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div id="header" style="height: 500px;"></div>',
+          '<nav id="navigation" class="navbar">',
+          '  <ul class="navbar-nav">',
+          '    <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
+          '    <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
+          '    <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
+          '  </ul>',
+          '</nav>',
+          '<div id="content" style="height: 200px; overflow-y: auto;">',
+          '  <div id="one" style="height: 100px;"></div>',
+          '  <div id="two" style="height: 100px;"></div>',
+          '  <div id="three" style="height: 100px;"></div>',
+          '  <div id="spacer" style="height: 100px;"></div>',
+          '</div>'
+        ].join('')
+
+        const negativeHeight = -10
+        const startOfSectionTwo = 101
+        const contentEl = fixtureEl.querySelector('#content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          target: '#navigation',
+          offset: contentEl.offsetTop
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        let firstTime = true
+
+        contentEl.addEventListener('scroll', () => {
+          const active = fixtureEl.querySelector('.active')
+
+          expect(spy).toHaveBeenCalled()
+          spy.calls.reset()
+          if (firstTime) {
+            expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+            expect(active.getAttribute('id')).toEqual('two-link')
+            firstTime = false
+            contentEl.scrollTop = negativeHeight
+          } else {
+            expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+            expect(active.getAttribute('id')).toEqual('one-link')
+            resolve()
+          }
+        })
+
+        contentEl.scrollTop = startOfSectionTwo
       })
-
-      contentEl.scrollTop = startOfSectionTwo
     })
 
-    it('should correctly select navigation element on backward scrolling when each target section height is 100%', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="navbar">',
-        '  <ul class="nav">',
-        '    <li class="nav-item"><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>',
-        '    <li class="nav-item"><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>',
-        '    <li class="nav-item"><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>',
-        '    <li class="nav-item"><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>',
-        '    <li class="nav-item"><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>',
-        '  </ul>',
-        '</nav>',
-        '<div class="content" style="position: relative; overflow: auto; height: 100px">',
-        '  <div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>',
-        '  <div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>',
-        '  <div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>',
-        '  <div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>',
-        '  <div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>',
-        '</div>'
-      ].join('')
-
-      const contentEl = fixtureEl.querySelector('.content')
-      const scrollSpy = new ScrollSpy(contentEl, {
-        offset: 0,
-        target: '.navbar'
-      })
-      const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
-      testElementIsActiveAfterScroll({
-        elementSelector: '#li-100-5',
-        targetSelector: '#div-100-5',
-        scrollSpy,
-        spy,
-        contentEl,
-        cb() {
-          contentEl.scrollTop = 0
-          testElementIsActiveAfterScroll({
-            elementSelector: '#li-100-4',
-            targetSelector: '#div-100-4',
-            scrollSpy,
-            spy,
-            contentEl,
-            cb() {
-              contentEl.scrollTop = 0
-              testElementIsActiveAfterScroll({
-                elementSelector: '#li-100-3',
-                targetSelector: '#div-100-3',
-                scrollSpy,
-                spy,
-                contentEl,
-                cb() {
-                  contentEl.scrollTop = 0
-                  testElementIsActiveAfterScroll({
-                    elementSelector: '#li-100-2',
-                    targetSelector: '#div-100-2',
-                    scrollSpy,
-                    spy,
-                    contentEl,
-                    cb() {
-                      contentEl.scrollTop = 0
-                      testElementIsActiveAfterScroll({
-                        elementSelector: '#li-100-1',
-                        targetSelector: '#div-100-1',
-                        scrollSpy,
-                        spy,
-                        contentEl,
-                        cb: done
-                      })
-                    }
-                  })
-                }
-              })
-            }
-          })
-        }
+    it('should correctly select navigation element on backward scrolling when each target section height is 100%', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="navbar">',
+          '  <ul class="nav">',
+          '    <li class="nav-item"><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>',
+          '    <li class="nav-item"><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>',
+          '    <li class="nav-item"><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>',
+          '    <li class="nav-item"><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>',
+          '    <li class="nav-item"><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>',
+          '  </ul>',
+          '</nav>',
+          '<div class="content" style="position: relative; overflow: auto; height: 100px">',
+          '  <div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>',
+          '  <div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>',
+          '  <div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>',
+          '  <div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>',
+          '  <div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>',
+          '</div>'
+        ].join('')
+
+        const contentEl = fixtureEl.querySelector('.content')
+        const scrollSpy = new ScrollSpy(contentEl, {
+          offset: 0,
+          target: '.navbar'
+        })
+        const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+        testElementIsActiveAfterScroll({
+          elementSelector: '#li-100-5',
+          targetSelector: '#div-100-5',
+          scrollSpy,
+          spy,
+          contentEl,
+          cb() {
+            contentEl.scrollTop = 0
+            testElementIsActiveAfterScroll({
+              elementSelector: '#li-100-4',
+              targetSelector: '#div-100-4',
+              scrollSpy,
+              spy,
+              contentEl,
+              cb() {
+                contentEl.scrollTop = 0
+                testElementIsActiveAfterScroll({
+                  elementSelector: '#li-100-3',
+                  targetSelector: '#div-100-3',
+                  scrollSpy,
+                  spy,
+                  contentEl,
+                  cb() {
+                    contentEl.scrollTop = 0
+                    testElementIsActiveAfterScroll({
+                      elementSelector: '#li-100-2',
+                      targetSelector: '#div-100-2',
+                      scrollSpy,
+                      spy,
+                      contentEl,
+                      cb() {
+                        contentEl.scrollTop = 0
+                        testElementIsActiveAfterScroll({
+                          elementSelector: '#li-100-1',
+                          targetSelector: '#div-100-1',
+                          scrollSpy,
+                          spy,
+                          contentEl,
+                          cb: resolve
+                        })
+                      }
+                    })
+                  }
+                })
+              }
+            })
+          }
+        })
       })
     })
 
index bafa08552681e2fb130f3b930caea4c14a2f3ea9..43adee53b2fdda650acc996e503d436a85da724b 100644 (file)
@@ -1,5 +1,5 @@
 import Tab from '../../src/tab'
-import { getFixture, clearFixture, jQueryMock } from '../helpers/fixture'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
 
 describe('Tab', () => {
   let fixtureEl
@@ -39,314 +39,336 @@ describe('Tab', () => {
   })
 
   describe('show', () => {
-    it('should activate element by tab id (using buttons, the preferred semantic way)', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav" role="tablist">',
-        '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
-        '  <li><button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button></li>',
-        '</ul>',
-        '<ul>',
-        '  <li id="home" role="tabpanel"></li>',
-        '  <li id="profile" role="tabpanel"></li>',
-        '</ul>'
-      ].join('')
-
-      const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
-      const tab = new Tab(profileTriggerEl)
+    it('should activate element by tab id (using buttons, the preferred semantic way)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav" role="tablist">',
+          '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+          '  <li><button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button></li>',
+          '</ul>',
+          '<ul>',
+          '  <li id="home" role="tabpanel"></li>',
+          '  <li id="profile" role="tabpanel"></li>',
+          '</ul>'
+        ].join('')
+
+        const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+        const tab = new Tab(profileTriggerEl)
+
+        profileTriggerEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
+          resolve()
+        })
 
-      profileTriggerEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
-        done()
+        tab.show()
       })
-
-      tab.show()
     })
 
-    it('should activate element by tab id (using links for tabs - not ideal, but still supported)', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav" role="tablist">',
-        '  <li><a href="#home" role="tab">Home</a></li>',
-        '  <li><a id="triggerProfile" href="#profile" role="tab">Profile</a></li>',
-        '</ul>',
-        '<ul>',
-        '  <li id="home" role="tabpanel"></li>',
-        '  <li id="profile" role="tabpanel"></li>',
-        '</ul>'
-      ].join('')
-
-      const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
-      const tab = new Tab(profileTriggerEl)
+    it('should activate element by tab id (using links for tabs - not ideal, but still supported)', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav" role="tablist">',
+          '  <li><a href="#home" role="tab">Home</a></li>',
+          '  <li><a id="triggerProfile" href="#profile" role="tab">Profile</a></li>',
+          '</ul>',
+          '<ul>',
+          '  <li id="home" role="tabpanel"></li>',
+          '  <li id="profile" role="tabpanel"></li>',
+          '</ul>'
+        ].join('')
+
+        const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+        const tab = new Tab(profileTriggerEl)
+
+        profileTriggerEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
+          resolve()
+        })
 
-      profileTriggerEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
-        done()
+        tab.show()
       })
-
-      tab.show()
     })
 
-    it('should activate element by tab id in ordered list', done => {
-      fixtureEl.innerHTML = [
-        '<ol class="nav nav-pills">',
-        '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
-        '  <li><button type="button" id="triggerProfile" href="#profile" role="tab">Profile</button></li>',
-        '</ol>',
-        '<ol>',
-        '  <li id="home" role="tabpanel"></li>',
-        '  <li id="profile" role="tabpanel"></li>',
-        '</ol>'
-      ].join('')
-
-      const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
-      const tab = new Tab(profileTriggerEl)
+    it('should activate element by tab id in ordered list', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ol class="nav nav-pills">',
+          '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+          '  <li><button type="button" id="triggerProfile" href="#profile" role="tab">Profile</button></li>',
+          '</ol>',
+          '<ol>',
+          '  <li id="home" role="tabpanel"></li>',
+          '  <li id="profile" role="tabpanel"></li>',
+          '</ol>'
+        ].join('')
+
+        const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+        const tab = new Tab(profileTriggerEl)
+
+        profileTriggerEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          resolve()
+        })
 
-      profileTriggerEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        done()
+        tab.show()
       })
-
-      tab.show()
     })
 
-    it('should activate element by tab id in nav list', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="nav">',
-        '  <button type="button" data-bs-target="#home" role="tab">Home</button>',
-        '  <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
-        '</nav>',
-        '<div>',
-        '  <div id="home" role="tabpanel"></div>',
-        '  <div id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
-      const tab = new Tab(profileTriggerEl)
+    it('should activate element by tab id in nav list', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="nav">',
+          '  <button type="button" data-bs-target="#home" role="tab">Home</button>',
+          '  <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
+          '</nav>',
+          '<div>',
+          '  <div id="home" role="tabpanel"></div>',
+          '  <div id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+        const tab = new Tab(profileTriggerEl)
+
+        profileTriggerEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          resolve()
+        })
 
-      profileTriggerEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        done()
+        tab.show()
       })
-
-      tab.show()
     })
 
-    it('should activate element by tab id in list group', done => {
-      fixtureEl.innerHTML = [
-        '<div class="list-group" role="tablist">',
-        '  <button type="button" data-bs-target="#home" role="tab">Home</button>',
-        '  <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
-        '</div>',
-        '<div>',
-        '  <div id="home" role="tabpanel"></div>',
-        '  <div id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
-      const tab = new Tab(profileTriggerEl)
+    it('should activate element by tab id in list group', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="list-group" role="tablist">',
+          '  <button type="button" data-bs-target="#home" role="tab">Home</button>',
+          '  <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
+          '</div>',
+          '<div>',
+          '  <div id="home" role="tabpanel"></div>',
+          '  <div id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+        const tab = new Tab(profileTriggerEl)
+
+        profileTriggerEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          resolve()
+        })
 
-      profileTriggerEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        done()
+        tab.show()
       })
-
-      tab.show()
     })
 
-    it('should not fire shown when show is prevented', done => {
-      fixtureEl.innerHTML = '<div class="nav"></div>'
+    it('should not fire shown when show is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div class="nav"></div>'
 
-      const navEl = fixtureEl.querySelector('div')
-      const tab = new Tab(navEl)
-      const expectDone = () => {
-        setTimeout(() => {
-          expect().nothing()
-          done()
-        }, 30)
-      }
+        const navEl = fixtureEl.querySelector('div')
+        const tab = new Tab(navEl)
+        const expectDone = () => {
+          setTimeout(() => {
+            expect().nothing()
+            resolve()
+          }, 30)
+        }
 
-      navEl.addEventListener('show.bs.tab', ev => {
-        ev.preventDefault()
-        expectDone()
-      })
+        navEl.addEventListener('show.bs.tab', ev => {
+          ev.preventDefault()
+          expectDone()
+        })
 
-      navEl.addEventListener('shown.bs.tab', () => {
-        throw new Error('should not trigger shown event')
-      })
+        navEl.addEventListener('shown.bs.tab', () => {
+          throw new Error('should not trigger shown event')
+        })
 
-      tab.show()
+        tab.show()
+      })
     })
 
-    it('should not fire shown when tab is already active', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const triggerActive = fixtureEl.querySelector('button.active')
-      const tab = new Tab(triggerActive)
+    it('should not fire shown when tab is already active', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const triggerActive = fixtureEl.querySelector('button.active')
+        const tab = new Tab(triggerActive)
+
+        triggerActive.addEventListener('shown.bs.tab', () => {
+          throw new Error('should not trigger shown event')
+        })
 
-      triggerActive.addEventListener('shown.bs.tab', () => {
-        throw new Error('should not trigger shown event')
+        tab.show()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 30)
       })
-
-      tab.show()
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 30)
     })
 
-    it('show and shown events should reference correct relatedTarget', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
-        '  <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
-      const secondTab = new Tab(secondTabTrigger)
+    it('show and shown events should reference correct relatedTarget', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+          '  <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+        const secondTab = new Tab(secondTabTrigger)
+
+        secondTabTrigger.addEventListener('show.bs.tab', ev => {
+          expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
+        })
 
-      secondTabTrigger.addEventListener('show.bs.tab', ev => {
-        expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
-      })
+        secondTabTrigger.addEventListener('shown.bs.tab', ev => {
+          expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
+          expect(secondTabTrigger.getAttribute('aria-selected')).toEqual('true')
+          expect(fixtureEl.querySelector('button:not(.active)').getAttribute('aria-selected')).toEqual('false')
+          resolve()
+        })
 
-      secondTabTrigger.addEventListener('shown.bs.tab', ev => {
-        expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
-        expect(secondTabTrigger.getAttribute('aria-selected')).toEqual('true')
-        expect(fixtureEl.querySelector('button:not(.active)').getAttribute('aria-selected')).toEqual('false')
-        done()
+        secondTab.show()
       })
-
-      secondTab.show()
     })
 
-    it('should fire hide and hidden events', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav" role="tablist">',
-        '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
-        '  <li><button type="button" data-bs-target="#profile">Profile</button></li>',
-        '</ul>'
-      ].join('')
+    it('should fire hide and hidden events', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav" role="tablist">',
+          '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+          '  <li><button type="button" data-bs-target="#profile">Profile</button></li>',
+          '</ul>'
+        ].join('')
 
-      const triggerList = fixtureEl.querySelectorAll('button')
-      const firstTab = new Tab(triggerList[0])
-      const secondTab = new Tab(triggerList[1])
+        const triggerList = fixtureEl.querySelectorAll('button')
+        const firstTab = new Tab(triggerList[0])
+        const secondTab = new Tab(triggerList[1])
 
-      let hideCalled = false
-      triggerList[0].addEventListener('shown.bs.tab', () => {
-        secondTab.show()
-      })
+        let hideCalled = false
+        triggerList[0].addEventListener('shown.bs.tab', () => {
+          secondTab.show()
+        })
 
-      triggerList[0].addEventListener('hide.bs.tab', ev => {
-        hideCalled = true
-        expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
-      })
+        triggerList[0].addEventListener('hide.bs.tab', ev => {
+          hideCalled = true
+          expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
+        })
 
-      triggerList[0].addEventListener('hidden.bs.tab', ev => {
-        expect(hideCalled).toBeTrue()
-        expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
-        done()
-      })
+        triggerList[0].addEventListener('hidden.bs.tab', ev => {
+          expect(hideCalled).toBeTrue()
+          expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
+          resolve()
+        })
 
-      firstTab.show()
+        firstTab.show()
+      })
     })
 
-    it('should not fire hidden when hide is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav" role="tablist">',
-        '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
-        '  <li><button type="button" data-bs-target="#profile" role="tab">Profile</button></li>',
-        '</ul>'
-      ].join('')
-
-      const triggerList = fixtureEl.querySelectorAll('button')
-      const firstTab = new Tab(triggerList[0])
-      const secondTab = new Tab(triggerList[1])
-      const expectDone = () => {
-        setTimeout(() => {
-          expect().nothing()
-          done()
-        }, 30)
-      }
+    it('should not fire hidden when hide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav" role="tablist">',
+          '  <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+          '  <li><button type="button" data-bs-target="#profile" role="tab">Profile</button></li>',
+          '</ul>'
+        ].join('')
+
+        const triggerList = fixtureEl.querySelectorAll('button')
+        const firstTab = new Tab(triggerList[0])
+        const secondTab = new Tab(triggerList[1])
+        const expectDone = () => {
+          setTimeout(() => {
+            expect().nothing()
+            resolve()
+          }, 30)
+        }
+
+        triggerList[0].addEventListener('shown.bs.tab', () => {
+          secondTab.show()
+        })
 
-      triggerList[0].addEventListener('shown.bs.tab', () => {
-        secondTab.show()
-      })
+        triggerList[0].addEventListener('hide.bs.tab', ev => {
+          ev.preventDefault()
+          expectDone()
+        })
 
-      triggerList[0].addEventListener('hide.bs.tab', ev => {
-        ev.preventDefault()
-        expectDone()
-      })
+        triggerList[0].addEventListener('hidden.bs.tab', () => {
+          throw new Error('should not trigger hidden')
+        })
 
-      triggerList[0].addEventListener('hidden.bs.tab', () => {
-        throw new Error('should not trigger hidden')
+        firstTab.show()
       })
-
-      firstTab.show()
     })
 
-    it('should handle removed tabs', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation">',
-        '    <a class="nav-link nav-tab" href="#profile" role="tab" data-bs-toggle="tab">',
-        '      <button class="btn-close" aria-label="Close"></button>',
-        '    </a>',
-        '  </li>',
-        '  <li class="nav-item" role="presentation">',
-        '    <a id="secondNav" class="nav-link nav-tab" href="#buzz" role="tab" data-bs-toggle="tab">',
-        '      <button class="btn-close" aria-label="Close"></button>',
-        '    </a>',
-        '  </li>',
-        '  <li class="nav-item" role="presentation">',
-        '    <a class="nav-link nav-tab" href="#references" role="tab" data-bs-toggle="tab">',
-        '      <button id="btnClose" class="btn-close" aria-label="Close"></button>',
-        '    </a>',
-        '  </li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div role="tabpanel" class="tab-pane fade show active" id="profile">test 1</div>',
-        '  <div role="tabpanel" class="tab-pane fade" id="buzz">test 2</div>',
-        '  <div role="tabpanel" class="tab-pane fade" id="references">test 3</div>',
-        '</div>'
-      ].join('')
-
-      const secondNavEl = fixtureEl.querySelector('#secondNav')
-      const btnCloseEl = fixtureEl.querySelector('#btnClose')
-      const secondNavTab = new Tab(secondNavEl)
+    it('should handle removed tabs', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation">',
+          '    <a class="nav-link nav-tab" href="#profile" role="tab" data-bs-toggle="tab">',
+          '      <button class="btn-close" aria-label="Close"></button>',
+          '    </a>',
+          '  </li>',
+          '  <li class="nav-item" role="presentation">',
+          '    <a id="secondNav" class="nav-link nav-tab" href="#buzz" role="tab" data-bs-toggle="tab">',
+          '      <button class="btn-close" aria-label="Close"></button>',
+          '    </a>',
+          '  </li>',
+          '  <li class="nav-item" role="presentation">',
+          '    <a class="nav-link nav-tab" href="#references" role="tab" data-bs-toggle="tab">',
+          '      <button id="btnClose" class="btn-close" aria-label="Close"></button>',
+          '    </a>',
+          '  </li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div role="tabpanel" class="tab-pane fade show active" id="profile">test 1</div>',
+          '  <div role="tabpanel" class="tab-pane fade" id="buzz">test 2</div>',
+          '  <div role="tabpanel" class="tab-pane fade" id="references">test 3</div>',
+          '</div>'
+        ].join('')
+
+        const secondNavEl = fixtureEl.querySelector('#secondNav')
+        const btnCloseEl = fixtureEl.querySelector('#btnClose')
+        const secondNavTab = new Tab(secondNavEl)
+
+        secondNavEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelectorAll('.nav-tab')).toHaveSize(2)
+          resolve()
+        })
 
-      secondNavEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelectorAll('.nav-tab')).toHaveSize(2)
-        done()
-      })
+        btnCloseEl.addEventListener('click', () => {
+          const linkEl = btnCloseEl.parentNode
+          const liEl = linkEl.parentNode
+          const tabId = linkEl.getAttribute('href')
+          const tabIdEl = fixtureEl.querySelector(tabId)
 
-      btnCloseEl.addEventListener('click', () => {
-        const linkEl = btnCloseEl.parentNode
-        const liEl = linkEl.parentNode
-        const tabId = linkEl.getAttribute('href')
-        const tabIdEl = fixtureEl.querySelector(tabId)
+          liEl.remove()
+          tabIdEl.remove()
+          secondNavTab.show()
+        })
 
-        liEl.remove()
-        tabIdEl.remove()
-        secondNavTab.show()
+        btnCloseEl.click()
       })
-
-      btnCloseEl.click()
     })
   })
 
@@ -464,27 +486,29 @@ describe('Tab', () => {
   })
 
   describe('data-api', () => {
-    it('should create dynamically a tab', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
-        '  <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-toggle="tab" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+    it('should create dynamically a tab', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+          '  <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-toggle="tab" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+
+        secondTabTrigger.addEventListener('shown.bs.tab', () => {
+          expect(secondTabTrigger).toHaveClass('active')
+          expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+          resolve()
+        })
 
-      secondTabTrigger.addEventListener('shown.bs.tab', () => {
-        expect(secondTabTrigger).toHaveClass('active')
-        expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
-        done()
+        secondTabTrigger.click()
       })
-
-      secondTabTrigger.click()
     })
 
     it('selected tab should deactivate previous selected link in dropdown', () => {
@@ -567,202 +591,216 @@ describe('Tab', () => {
       expect(fixtureEl.querySelector('.nav-link')).not.toHaveClass('active')
     })
 
-    it('should handle nested tabs', done => {
-      fixtureEl.innerHTML = [
-        '<nav class="nav nav-tabs" role="tablist">',
-        '  <button type="button" id="tab1" data-bs-target="#x-tab1" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab1">Tab 1</button>',
-        '  <button type="button" data-bs-target="#x-tab2" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab2" aria-selected="true">Tab 2</button>',
-        '  <button type="button" data-bs-target="#x-tab3" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab3">Tab 3</button>',
-        '</nav>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane" id="x-tab1" role="tabpanel">',
-        '    <nav class="nav nav-tabs" role="tablist">',
-        '      <button type="button" data-bs-target="#nested-tab1" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab1" aria-selected="true">Nested Tab 1</button>',
-        '      <button type="button" id="tabNested2" data-bs-target="#nested-tab2" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-profile">Nested Tab2</button>',
-        '    </nav>',
-        '    <div class="tab-content">',
-        '      <div class="tab-pane active" id="nested-tab1" role="tabpanel">Nested Tab1 Content</div>',
-        '      <div class="tab-pane" id="nested-tab2" role="tabpanel">Nested Tab2 Content</div>',
-        '    </div>',
-        '  </div>',
-        '  <div class="tab-pane active" id="x-tab2" role="tabpanel">Tab2 Content</div>',
-        '  <div class="tab-pane" id="x-tab3" role="tabpanel">Tab3 Content</div>',
-        '</div>'
-      ].join('')
-
-      const tab1El = fixtureEl.querySelector('#tab1')
-      const tabNested2El = fixtureEl.querySelector('#tabNested2')
-      const xTab1El = fixtureEl.querySelector('#x-tab1')
+    it('should handle nested tabs', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<nav class="nav nav-tabs" role="tablist">',
+          '  <button type="button" id="tab1" data-bs-target="#x-tab1" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab1">Tab 1</button>',
+          '  <button type="button" data-bs-target="#x-tab2" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab2" aria-selected="true">Tab 2</button>',
+          '  <button type="button" data-bs-target="#x-tab3" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab3">Tab 3</button>',
+          '</nav>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane" id="x-tab1" role="tabpanel">',
+          '    <nav class="nav nav-tabs" role="tablist">',
+          '      <button type="button" data-bs-target="#nested-tab1" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab1" aria-selected="true">Nested Tab 1</button>',
+          '      <button type="button" id="tabNested2" data-bs-target="#nested-tab2" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-profile">Nested Tab2</button>',
+          '    </nav>',
+          '    <div class="tab-content">',
+          '      <div class="tab-pane active" id="nested-tab1" role="tabpanel">Nested Tab1 Content</div>',
+          '      <div class="tab-pane" id="nested-tab2" role="tabpanel">Nested Tab2 Content</div>',
+          '    </div>',
+          '  </div>',
+          '  <div class="tab-pane active" id="x-tab2" role="tabpanel">Tab2 Content</div>',
+          '  <div class="tab-pane" id="x-tab3" role="tabpanel">Tab3 Content</div>',
+          '</div>'
+        ].join('')
+
+        const tab1El = fixtureEl.querySelector('#tab1')
+        const tabNested2El = fixtureEl.querySelector('#tabNested2')
+        const xTab1El = fixtureEl.querySelector('#x-tab1')
+
+        tabNested2El.addEventListener('shown.bs.tab', () => {
+          expect(xTab1El).toHaveClass('active')
+          resolve()
+        })
 
-      tabNested2El.addEventListener('shown.bs.tab', () => {
-        expect(xTab1El).toHaveClass('active')
-        done()
-      })
+        tab1El.addEventListener('shown.bs.tab', () => {
+          expect(xTab1El).toHaveClass('active')
+          tabNested2El.click()
+        })
 
-      tab1El.addEventListener('shown.bs.tab', () => {
-        expect(xTab1El).toHaveClass('active')
-        tabNested2El.click()
+        tab1El.click()
       })
-
-      tab1El.click()
     })
 
-    it('should not remove fade class if no active pane is present', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><button type="button" id="tab-home" data-bs-target="#home" class="nav-link" data-bs-toggle="tab" role="tab">Home</button></li>',
-        '  <li class="nav-item" role="presentation"><button type="button" id="tab-profile" data-bs-target="#profile" class="nav-link" data-bs-toggle="tab" role="tab">Profile</button></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane fade" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane fade" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
-
-      const triggerTabProfileEl = fixtureEl.querySelector('#tab-profile')
-      const triggerTabHomeEl = fixtureEl.querySelector('#tab-home')
-      const tabProfileEl = fixtureEl.querySelector('#profile')
-      const tabHomeEl = fixtureEl.querySelector('#home')
+    it('should not remove fade class if no active pane is present', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><button type="button" id="tab-home" data-bs-target="#home" class="nav-link" data-bs-toggle="tab" role="tab">Home</button></li>',
+          '  <li class="nav-item" role="presentation"><button type="button" id="tab-profile" data-bs-target="#profile" class="nav-link" data-bs-toggle="tab" role="tab">Profile</button></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane fade" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane fade" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const triggerTabProfileEl = fixtureEl.querySelector('#tab-profile')
+        const triggerTabHomeEl = fixtureEl.querySelector('#tab-home')
+        const tabProfileEl = fixtureEl.querySelector('#profile')
+        const tabHomeEl = fixtureEl.querySelector('#home')
+
+        triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
+          expect(tabProfileEl).toHaveClass('fade')
+          expect(tabProfileEl).toHaveClass('show')
 
-      triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
-        expect(tabProfileEl).toHaveClass('fade')
-        expect(tabProfileEl).toHaveClass('show')
+          triggerTabHomeEl.addEventListener('shown.bs.tab', () => {
+            expect(tabProfileEl).toHaveClass('fade')
+            expect(tabProfileEl).not.toHaveClass('show')
 
-        triggerTabHomeEl.addEventListener('shown.bs.tab', () => {
-          expect(tabProfileEl).toHaveClass('fade')
-          expect(tabProfileEl).not.toHaveClass('show')
+            expect(tabHomeEl).toHaveClass('fade')
+            expect(tabHomeEl).toHaveClass('show')
 
-          expect(tabHomeEl).toHaveClass('fade')
-          expect(tabHomeEl).toHaveClass('show')
+            resolve()
+          })
 
-          done()
+          triggerTabHomeEl.click()
         })
 
-        triggerTabHomeEl.click()
+        triggerTabProfileEl.click()
       })
-
-      triggerTabProfileEl.click()
     })
 
-    it('should not add show class to tab panes if there is no `.fade` class', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation">',
-        '    <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
-        '  </li>',
-        '  <li class="nav-item" role="presentation">',
-        '    <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
-        '  </li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div role="tabpanel" class="tab-pane" id="home">test 1</div>',
-        '  <div role="tabpanel" class="tab-pane" id="profile">test 2</div>',
-        '</div>'
-      ].join('')
-
-      const secondNavEl = fixtureEl.querySelector('#secondNav')
+    it('should not add show class to tab panes if there is no `.fade` class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation">',
+          '    <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
+          '  </li>',
+          '  <li class="nav-item" role="presentation">',
+          '    <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
+          '  </li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div role="tabpanel" class="tab-pane" id="home">test 1</div>',
+          '  <div role="tabpanel" class="tab-pane" id="profile">test 2</div>',
+          '</div>'
+        ].join('')
+
+        const secondNavEl = fixtureEl.querySelector('#secondNav')
+
+        secondNavEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelectorAll('.show')).toHaveSize(0)
+          resolve()
+        })
 
-      secondNavEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelectorAll('.show')).toHaveSize(0)
-        done()
+        secondNavEl.click()
       })
-
-      secondNavEl.click()
     })
 
-    it('should add show class to tab panes if there is a `.fade` class', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation">',
-        '    <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
-        '  </li>',
-        '  <li class="nav-item" role="presentation">',
-        '    <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
-        '  </li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div role="tabpanel" class="tab-pane fade" id="home">test 1</div>',
-        '  <div role="tabpanel" class="tab-pane fade" id="profile">test 2</div>',
-        '</div>'
-      ].join('')
-
-      const secondNavEl = fixtureEl.querySelector('#secondNav')
+    it('should add show class to tab panes if there is a `.fade` class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation">',
+          '    <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
+          '  </li>',
+          '  <li class="nav-item" role="presentation">',
+          '    <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
+          '  </li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div role="tabpanel" class="tab-pane fade" id="home">test 1</div>',
+          '  <div role="tabpanel" class="tab-pane fade" id="profile">test 2</div>',
+          '</div>'
+        ].join('')
+
+        const secondNavEl = fixtureEl.querySelector('#secondNav')
+
+        secondNavEl.addEventListener('shown.bs.tab', () => {
+          expect(fixtureEl.querySelectorAll('.show')).toHaveSize(1)
+          resolve()
+        })
 
-      secondNavEl.addEventListener('shown.bs.tab', () => {
-        expect(fixtureEl.querySelectorAll('.show')).toHaveSize(1)
-        done()
+        secondNavEl.click()
       })
-
-      secondNavEl.click()
     })
 
-    it('should prevent default when the trigger is <a> or <area>', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav" role="tablist">',
-        '  <li><a type="button" href="#test"  class="active" role="tab" data-bs-toggle="tab">Home</a></li>',
-        '  <li><a type="button" href="#test2" role="tab" data-bs-toggle="tab">Home</a></li>',
-        '</ul>'
-      ].join('')
+    it('should prevent default when the trigger is <a> or <area>', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav" role="tablist">',
+          '  <li><a type="button" href="#test"  class="active" role="tab" data-bs-toggle="tab">Home</a></li>',
+          '  <li><a type="button" href="#test2" role="tab" data-bs-toggle="tab">Home</a></li>',
+          '</ul>'
+        ].join('')
 
-      const tabEl = fixtureEl.querySelector('[href="#test2"]')
-      spyOn(Event.prototype, 'preventDefault').and.callThrough()
+        const tabEl = fixtureEl.querySelector('[href="#test2"]')
+        spyOn(Event.prototype, 'preventDefault').and.callThrough()
 
-      tabEl.addEventListener('shown.bs.tab', () => {
-        expect(tabEl).toHaveClass('active')
-        expect(Event.prototype.preventDefault).toHaveBeenCalled()
-        done()
-      })
+        tabEl.addEventListener('shown.bs.tab', () => {
+          expect(tabEl).toHaveClass('active')
+          expect(Event.prototype.preventDefault).toHaveBeenCalled()
+          resolve()
+        })
 
-      tabEl.click()
+        tabEl.click()
+      })
     })
 
-    it('should not fire shown when tab has disabled attribute', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
-        '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" disabled role="tab">Profile</button></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
+    it('should not fire shown when tab has disabled attribute', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+          '  <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" disabled role="tab">Profile</button></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
+
+        const triggerDisabled = fixtureEl.querySelector('button[disabled]')
+        triggerDisabled.addEventListener('shown.bs.tab', () => {
+          throw new Error('should not trigger shown event')
+        })
 
-      const triggerDisabled = fixtureEl.querySelector('button[disabled]')
-      triggerDisabled.addEventListener('shown.bs.tab', () => {
-        throw new Error('should not trigger shown event')
+        triggerDisabled.click()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 30)
       })
-
-      triggerDisabled.click()
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 30)
     })
 
-    it('should not fire shown when tab has disabled class', done => {
-      fixtureEl.innerHTML = [
-        '<ul class="nav nav-tabs" role="tablist">',
-        '  <li class="nav-item" role="presentation"><a href="#home" class="nav-link active" role="tab" aria-selected="true">Home</a></li>',
-        '  <li class="nav-item" role="presentation"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>',
-        '</ul>',
-        '<div class="tab-content">',
-        '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
-        '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
-        '</div>'
-      ].join('')
+    it('should not fire shown when tab has disabled class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<ul class="nav nav-tabs" role="tablist">',
+          '  <li class="nav-item" role="presentation"><a href="#home" class="nav-link active" role="tab" aria-selected="true">Home</a></li>',
+          '  <li class="nav-item" role="presentation"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>',
+          '</ul>',
+          '<div class="tab-content">',
+          '  <div class="tab-pane active" id="home" role="tabpanel"></div>',
+          '  <div class="tab-pane" id="profile" role="tabpanel"></div>',
+          '</div>'
+        ].join('')
 
-      const triggerDisabled = fixtureEl.querySelector('a.disabled')
+        const triggerDisabled = fixtureEl.querySelector('a.disabled')
 
-      triggerDisabled.addEventListener('shown.bs.tab', () => {
-        throw new Error('should not trigger shown event')
-      })
+        triggerDisabled.addEventListener('shown.bs.tab', () => {
+          throw new Error('should not trigger shown event')
+        })
 
-      triggerDisabled.click()
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 30)
+        triggerDisabled.click()
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 30)
+      })
     })
   })
 })
index c4ea43808c5271b536c9a3fe753d0ceb1f7d7aaa..9134a8410de662c369d6351071ebc138405b4148 100644 (file)
@@ -1,5 +1,5 @@
 import Toast from '../../src/toast'
-import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
+import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
 
 describe('Toast', () => {
   let fixtureEl
@@ -36,52 +36,56 @@ describe('Toast', () => {
       expect(toastByElement._element).toEqual(toastEl)
     })
 
-    it('should allow to config in js', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should allow to config in js', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('div')
+        const toast = new Toast(toastEl, {
+          delay: 1
+        })
 
-      const toastEl = fixtureEl.querySelector('div')
-      const toast = new Toast(toastEl, {
-        delay: 1
-      })
+        toastEl.addEventListener('shown.bs.toast', () => {
+          expect(toastEl).toHaveClass('show')
+          resolve()
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        expect(toastEl).toHaveClass('show')
-        done()
+        toast.show()
       })
-
-      toast.show()
     })
 
-    it('should close toast when close element with data-bs-dismiss attribute is set', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
-        '  <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
-        '</div>'
-      ].join('')
+    it('should close toast when close element with data-bs-dismiss attribute is set', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
+          '  <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
+          '</div>'
+        ].join('')
 
-      const toastEl = fixtureEl.querySelector('div')
-      const toast = new Toast(toastEl)
+        const toastEl = fixtureEl.querySelector('div')
+        const toast = new Toast(toastEl)
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        expect(toastEl).toHaveClass('show')
+        toastEl.addEventListener('shown.bs.toast', () => {
+          expect(toastEl).toHaveClass('show')
 
-        const button = toastEl.querySelector('.btn-close')
+          const button = toastEl.querySelector('.btn-close')
 
-        button.click()
-      })
+          button.click()
+        })
 
-      toastEl.addEventListener('hidden.bs.toast', () => {
-        expect(toastEl).not.toHaveClass('show')
-        done()
-      })
+        toastEl.addEventListener('hidden.bs.toast', () => {
+          expect(toastEl).not.toHaveClass('show')
+          resolve()
+        })
 
-      toast.show()
+        toast.show()
+      })
     })
   })
 
@@ -111,304 +115,324 @@ describe('Toast', () => {
   })
 
   describe('show', () => {
-    it('should auto hide', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
-
-      toastEl.addEventListener('hidden.bs.toast', () => {
-        expect(toastEl).not.toHaveClass('show')
-        done()
-      })
-
-      toast.show()
-    })
-
-    it('should not add fade class', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+    it('should auto hide', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
+
+        toastEl.addEventListener('hidden.bs.toast', () => {
+          expect(toastEl).not.toHaveClass('show')
+          resolve()
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        expect(toastEl).not.toHaveClass('fade')
-        done()
+        toast.show()
       })
-
-      toast.show()
     })
 
-    it('should not trigger shown if show is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not add fade class', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
-
-      const assertDone = () => {
-        setTimeout(() => {
-          expect(toastEl).not.toHaveClass('show')
-          done()
-        }, 20)
-      }
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-      toastEl.addEventListener('show.bs.toast', event => {
-        event.preventDefault()
-        assertDone()
-      })
+        toastEl.addEventListener('shown.bs.toast', () => {
+          expect(toastEl).not.toHaveClass('fade')
+          resolve()
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        throw new Error('shown event should not be triggered if show is prevented')
+        toast.show()
       })
-
-      toast.show()
     })
 
-    it('should clear timeout if toast is shown again before it is hidden', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+    it('should not trigger shown if show is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
+
+        const assertDone = () => {
+          setTimeout(() => {
+            expect(toastEl).not.toHaveClass('show')
+            resolve()
+          }, 20)
+        }
+
+        toastEl.addEventListener('show.bs.toast', event => {
+          event.preventDefault()
+          assertDone()
+        })
 
-      setTimeout(() => {
-        toast._config.autohide = false
         toastEl.addEventListener('shown.bs.toast', () => {
-          expect(toast._clearTimeout).toHaveBeenCalled()
-          expect(toast._timeout).toBeNull()
-          done()
+          throw new Error('shown event should not be triggered if show is prevented')
         })
-        toast.show()
-      }, toast._config.delay / 2)
 
-      spyOn(toast, '_clearTimeout').and.callThrough()
-
-      toast.show()
+        toast.show()
+      })
     })
 
-    it('should clear timeout if toast is interacted with mouse', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
-      const spy = spyOn(toast, '_clearTimeout').and.callThrough()
+    it('should clear timeout if toast is shown again before it is hidden', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      setTimeout(() => {
-        spy.calls.reset()
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-        toastEl.addEventListener('mouseover', () => {
-          expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
-          expect(toast._timeout).toBeNull()
-          done()
-        })
+        setTimeout(() => {
+          toast._config.autohide = false
+          toastEl.addEventListener('shown.bs.toast', () => {
+            expect(toast._clearTimeout).toHaveBeenCalled()
+            expect(toast._timeout).toBeNull()
+            resolve()
+          })
+          toast.show()
+        }, toast._config.delay / 2)
 
-        const mouseOverEvent = createEvent('mouseover')
-        toastEl.dispatchEvent(mouseOverEvent)
-      }, toast._config.delay / 2)
+        spyOn(toast, '_clearTimeout').and.callThrough()
 
-      toast.show()
+        toast.show()
+      })
     })
 
-    it('should clear timeout if toast is interacted with keyboard', done => {
-      fixtureEl.innerHTML = [
-        '<button id="outside-focusable">outside focusable</button>',
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '    <button>with a button</button>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should clear timeout if toast is interacted with mouse', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
 
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
-      const spy = spyOn(toast, '_clearTimeout').and.callThrough()
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
+        const spy = spyOn(toast, '_clearTimeout').and.callThrough()
 
-      setTimeout(() => {
-        spy.calls.reset()
+        setTimeout(() => {
+          spy.calls.reset()
 
-        toastEl.addEventListener('focusin', () => {
-          expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
-          expect(toast._timeout).toBeNull()
-          done()
-        })
+          toastEl.addEventListener('mouseover', () => {
+            expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
+            expect(toast._timeout).toBeNull()
+            resolve()
+          })
 
-        const insideFocusable = toastEl.querySelector('button')
-        insideFocusable.focus()
-      }, toast._config.delay / 2)
+          const mouseOverEvent = createEvent('mouseover')
+          toastEl.dispatchEvent(mouseOverEvent)
+        }, toast._config.delay / 2)
 
-      toast.show()
+        toast.show()
+      })
     })
 
-    it('should still auto hide after being interacted with mouse and keyboard', done => {
-      fixtureEl.innerHTML = [
-        '<button id="outside-focusable">outside focusable</button>',
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '    <button>with a button</button>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should clear timeout if toast is interacted with keyboard', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="outside-focusable">outside focusable</button>',
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '    <button>with a button</button>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
+        const spy = spyOn(toast, '_clearTimeout').and.callThrough()
 
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+        setTimeout(() => {
+          spy.calls.reset()
+
+          toastEl.addEventListener('focusin', () => {
+            expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
+            expect(toast._timeout).toBeNull()
+            resolve()
+          })
 
-      setTimeout(() => {
-        toastEl.addEventListener('mouseover', () => {
           const insideFocusable = toastEl.querySelector('button')
           insideFocusable.focus()
-        })
+        }, toast._config.delay / 2)
 
-        toastEl.addEventListener('focusin', () => {
-          const mouseOutEvent = createEvent('mouseout')
-          toastEl.dispatchEvent(mouseOutEvent)
-        })
+        toast.show()
+      })
+    })
 
-        toastEl.addEventListener('mouseout', () => {
-          const outsideFocusable = document.getElementById('outside-focusable')
-          outsideFocusable.focus()
-        })
+    it('should still auto hide after being interacted with mouse and keyboard', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="outside-focusable">outside focusable</button>',
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '    <button>with a button</button>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-        toastEl.addEventListener('focusout', () => {
-          expect(toast._timeout).not.toBeNull()
-          done()
-        })
-
-        const mouseOverEvent = createEvent('mouseover')
-        toastEl.dispatchEvent(mouseOverEvent)
-      }, toast._config.delay / 2)
+        setTimeout(() => {
+          toastEl.addEventListener('mouseover', () => {
+            const insideFocusable = toastEl.querySelector('button')
+            insideFocusable.focus()
+          })
+
+          toastEl.addEventListener('focusin', () => {
+            const mouseOutEvent = createEvent('mouseout')
+            toastEl.dispatchEvent(mouseOutEvent)
+          })
+
+          toastEl.addEventListener('mouseout', () => {
+            const outsideFocusable = document.getElementById('outside-focusable')
+            outsideFocusable.focus()
+          })
+
+          toastEl.addEventListener('focusout', () => {
+            expect(toast._timeout).not.toBeNull()
+            resolve()
+          })
+
+          const mouseOverEvent = createEvent('mouseover')
+          toastEl.dispatchEvent(mouseOverEvent)
+        }, toast._config.delay / 2)
 
-      toast.show()
+        toast.show()
+      })
     })
 
-    it('should not auto hide if focus leaves but mouse pointer remains inside', done => {
-      fixtureEl.innerHTML = [
-        '<button id="outside-focusable">outside focusable</button>',
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '    <button>with a button</button>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+    it('should not auto hide if focus leaves but mouse pointer remains inside', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="outside-focusable">outside focusable</button>',
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '    <button>with a button</button>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-      setTimeout(() => {
-        toastEl.addEventListener('mouseover', () => {
-          const insideFocusable = toastEl.querySelector('button')
-          insideFocusable.focus()
-        })
+        setTimeout(() => {
+          toastEl.addEventListener('mouseover', () => {
+            const insideFocusable = toastEl.querySelector('button')
+            insideFocusable.focus()
+          })
 
-        toastEl.addEventListener('focusin', () => {
-          const outsideFocusable = document.getElementById('outside-focusable')
-          outsideFocusable.focus()
-        })
+          toastEl.addEventListener('focusin', () => {
+            const outsideFocusable = document.getElementById('outside-focusable')
+            outsideFocusable.focus()
+          })
 
-        toastEl.addEventListener('focusout', () => {
-          expect(toast._timeout).toBeNull()
-          done()
-        })
+          toastEl.addEventListener('focusout', () => {
+            expect(toast._timeout).toBeNull()
+            resolve()
+          })
 
-        const mouseOverEvent = createEvent('mouseover')
-        toastEl.dispatchEvent(mouseOverEvent)
-      }, toast._config.delay / 2)
+          const mouseOverEvent = createEvent('mouseover')
+          toastEl.dispatchEvent(mouseOverEvent)
+        }, toast._config.delay / 2)
 
-      toast.show()
+        toast.show()
+      })
     })
 
-    it('should not auto hide if mouse pointer leaves but focus remains inside', done => {
-      fixtureEl.innerHTML = [
-        '<button id="outside-focusable">outside focusable</button>',
-        '<div class="toast">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '    <button>with a button</button>',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should not auto hide if mouse pointer leaves but focus remains inside', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<button id="outside-focusable">outside focusable</button>',
+          '<div class="toast">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '    <button>with a button</button>',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
-
-      setTimeout(() => {
-        toastEl.addEventListener('mouseover', () => {
-          const insideFocusable = toastEl.querySelector('button')
-          insideFocusable.focus()
-        })
+        setTimeout(() => {
+          toastEl.addEventListener('mouseover', () => {
+            const insideFocusable = toastEl.querySelector('button')
+            insideFocusable.focus()
+          })
 
-        toastEl.addEventListener('focusin', () => {
-          const mouseOutEvent = createEvent('mouseout')
-          toastEl.dispatchEvent(mouseOutEvent)
-        })
+          toastEl.addEventListener('focusin', () => {
+            const mouseOutEvent = createEvent('mouseout')
+            toastEl.dispatchEvent(mouseOutEvent)
+          })
 
-        toastEl.addEventListener('mouseout', () => {
-          expect(toast._timeout).toBeNull()
-          done()
-        })
+          toastEl.addEventListener('mouseout', () => {
+            expect(toast._timeout).toBeNull()
+            resolve()
+          })
 
-        const mouseOverEvent = createEvent('mouseover')
-        toastEl.dispatchEvent(mouseOverEvent)
-      }, toast._config.delay / 2)
+          const mouseOverEvent = createEvent('mouseover')
+          toastEl.dispatchEvent(mouseOverEvent)
+        }, toast._config.delay / 2)
 
-      toast.show()
+        toast.show()
+      })
     })
   })
 
   describe('hide', () => {
-    it('should allow to hide toast manually', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1" data-bs-autohide="false">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should allow to hide toast manually', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1" data-bs-autohide="false">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
 
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+        toastEl.addEventListener('shown.bs.toast', () => {
+          toast.hide()
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        toast.hide()
-      })
+        toastEl.addEventListener('hidden.bs.toast', () => {
+          expect(toastEl).not.toHaveClass('show')
+          resolve()
+        })
 
-      toastEl.addEventListener('hidden.bs.toast', () => {
-        expect(toastEl).not.toHaveClass('show')
-        done()
+        toast.show()
       })
-
-      toast.show()
     })
 
     it('should do nothing when we call hide on a non shown toast', () => {
@@ -424,39 +448,41 @@ describe('Toast', () => {
       expect(toastEl.classList.contains).toHaveBeenCalled()
     })
 
-    it('should not trigger hidden if hide is prevented', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const toastEl = fixtureEl.querySelector('.toast')
-      const toast = new Toast(toastEl)
+    it('should not trigger hidden if hide is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('.toast')
+        const toast = new Toast(toastEl)
+
+        const assertDone = () => {
+          setTimeout(() => {
+            expect(toastEl).toHaveClass('show')
+            resolve()
+          }, 20)
+        }
 
-      const assertDone = () => {
-        setTimeout(() => {
-          expect(toastEl).toHaveClass('show')
-          done()
-        }, 20)
-      }
+        toastEl.addEventListener('shown.bs.toast', () => {
+          toast.hide()
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        toast.hide()
-      })
+        toastEl.addEventListener('hide.bs.toast', event => {
+          event.preventDefault()
+          assertDone()
+        })
 
-      toastEl.addEventListener('hide.bs.toast', event => {
-        event.preventDefault()
-        assertDone()
-      })
+        toastEl.addEventListener('hidden.bs.toast', () => {
+          throw new Error('hidden event should not be triggered if hide is prevented')
+        })
 
-      toastEl.addEventListener('hidden.bs.toast', () => {
-        throw new Error('hidden event should not be triggered if hide is prevented')
+        toast.show()
       })
-
-      toast.show()
     })
   })
 
@@ -475,34 +501,36 @@ describe('Toast', () => {
       expect(Toast.getInstance(toastEl)).toBeNull()
     })
 
-    it('should allow to destroy toast and hide it before that', done => {
-      fixtureEl.innerHTML = [
-        '<div class="toast" data-bs-delay="0" data-bs-autohide="false">',
-        '  <div class="toast-body">',
-        '    a simple toast',
-        '  </div>',
-        '</div>'
-      ].join('')
+    it('should allow to destroy toast and hide it before that', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<div class="toast" data-bs-delay="0" data-bs-autohide="false">',
+          '  <div class="toast-body">',
+          '    a simple toast',
+          '  </div>',
+          '</div>'
+        ].join('')
+
+        const toastEl = fixtureEl.querySelector('div')
+        const toast = new Toast(toastEl)
+        const expected = () => {
+          expect(toastEl).toHaveClass('show')
+          expect(Toast.getInstance(toastEl)).not.toBeNull()
 
-      const toastEl = fixtureEl.querySelector('div')
-      const toast = new Toast(toastEl)
-      const expected = () => {
-        expect(toastEl).toHaveClass('show')
-        expect(Toast.getInstance(toastEl)).not.toBeNull()
+          toast.dispose()
 
-        toast.dispose()
+          expect(Toast.getInstance(toastEl)).toBeNull()
+          expect(toastEl).not.toHaveClass('show')
 
-        expect(Toast.getInstance(toastEl)).toBeNull()
-        expect(toastEl).not.toHaveClass('show')
+          resolve()
+        }
 
-        done()
-      }
+        toastEl.addEventListener('shown.bs.toast', () => {
+          setTimeout(expected, 1)
+        })
 
-      toastEl.addEventListener('shown.bs.toast', () => {
-        setTimeout(expected, 1)
+        toast.show()
       })
-
-      toast.show()
     })
   })
 
index 3feacf7b44459a30add14106d5a015babdb73baa..4a9a6d3bb0b270f3526fb6af388bb46104894a45 100644 (file)
@@ -94,52 +94,56 @@ describe('Tooltip', () => {
       expect(tooltip._config.content).toEqual('7')
     })
 
-    it('should enable selector delegation', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should enable selector delegation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
+
+        const containerEl = fixtureEl.querySelector('div')
+        const tooltipContainer = new Tooltip(containerEl, {
+          selector: 'a[rel="tooltip"]',
+          trigger: 'click'
+        })
 
-      const containerEl = fixtureEl.querySelector('div')
-      const tooltipContainer = new Tooltip(containerEl, {
-        selector: 'a[rel="tooltip"]',
-        trigger: 'click'
-      })
+        containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+        const tooltipInContainerEl = containerEl.querySelector('a')
 
-      const tooltipInContainerEl = containerEl.querySelector('a')
+        tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
+          tooltipContainer.dispose()
+          resolve()
+        })
 
-      tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
-        tooltipContainer.dispose()
-        done()
+        tooltipInContainerEl.click()
       })
-
-      tooltipInContainerEl.click()
     })
 
-    it('should create offset modifier when offset is passed as a function', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
+    it('should create offset modifier when offset is passed as a function', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
 
-      const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        offset: getOffset,
-        popperConfig: {
-          onFirstUpdate: state => {
-            expect(getOffset).toHaveBeenCalledWith({
-              popper: state.rects.popper,
-              reference: state.rects.reference,
-              placement: state.placement
-            }, tooltipEl)
-            done()
+        const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          offset: getOffset,
+          popperConfig: {
+            onFirstUpdate: state => {
+              expect(getOffset).toHaveBeenCalledWith({
+                popper: state.rects.popper,
+                reference: state.rects.reference,
+                placement: state.placement
+              }, tooltipEl)
+              resolve()
+            }
           }
-        }
-      })
+        })
 
-      const offset = tooltip._getOffset()
+        const offset = tooltip._getOffset()
 
-      expect(offset).toEqual(jasmine.any(Function))
+        expect(offset).toEqual(jasmine.any(Function))
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
     it('should create offset modifier when offset option is passed in data attribute', () => {
@@ -192,42 +196,46 @@ describe('Tooltip', () => {
   })
 
   describe('enable', () => {
-    it('should enable a tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should enable a tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltip.enable()
+        tooltip.enable()
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
   })
 
   describe('disable', () => {
-    it('should disable tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should disable tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltip.disable()
+        tooltip.disable()
 
-      tooltipEl.addEventListener('show.bs.tooltip', () => {
-        throw new Error('should not show a disabled tooltip')
-      })
+        tooltipEl.addEventListener('show.bs.tooltip', () => {
+          throw new Error('should not show a disabled tooltip')
+        })
 
-      tooltip.show()
+        tooltip.show()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
   })
 
@@ -247,96 +255,106 @@ describe('Tooltip', () => {
   })
 
   describe('toggle', () => {
-    it('should do nothing if disabled', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should do nothing if disabled', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltip.disable()
+        tooltip.disable()
 
-      tooltipEl.addEventListener('show.bs.tooltip', () => {
-        throw new Error('should not show a disabled tooltip')
-      })
+        tooltipEl.addEventListener('show.bs.tooltip', () => {
+          throw new Error('should not show a disabled tooltip')
+        })
 
-      tooltip.toggle()
+        tooltip.toggle()
 
-      setTimeout(() => {
-        expect().nothing()
-        done()
-      }, 10)
+        setTimeout(() => {
+          expect().nothing()
+          resolve()
+        }, 10)
+      })
     })
 
-    it('should show a tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
+          resolve()
+        })
 
-      tooltip.toggle()
+        tooltip.toggle()
+      })
     })
 
-    it('should call toggle and show the tooltip when trigger is "click"', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should call toggle and show the tooltip when trigger is "click"', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        trigger: 'click'
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          trigger: 'click'
+        })
 
-      spyOn(tooltip, 'toggle').and.callThrough()
+        spyOn(tooltip, 'toggle').and.callThrough()
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(tooltip.toggle).toHaveBeenCalled()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(tooltip.toggle).toHaveBeenCalled()
+          resolve()
+        })
 
-      tooltipEl.click()
+        tooltipEl.click()
+      })
     })
 
-    it('should hide a tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should hide a tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
+
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          tooltip.toggle()
+        })
+
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).toBeNull()
+          resolve()
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
         tooltip.toggle()
       })
+    })
 
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).toBeNull()
-        done()
-      })
+    it('should call toggle and hide the tooltip when trigger is "click"', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      tooltip.toggle()
-    })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          trigger: 'click'
+        })
 
-    it('should call toggle and hide the tooltip when trigger is "click"', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+        spyOn(tooltip, 'toggle').and.callThrough()
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        trigger: 'click'
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          tooltipEl.click()
+        })
 
-      spyOn(tooltip, 'toggle').and.callThrough()
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          expect(tooltip.toggle).toHaveBeenCalled()
+          resolve()
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
         tooltipEl.click()
       })
-
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        expect(tooltip.toggle).toHaveBeenCalled()
-        done()
-      })
-
-      tooltipEl.click()
     })
   })
 
@@ -367,239 +385,263 @@ describe('Tooltip', () => {
       expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
     })
 
-    it('should destroy a tooltip after it is shown and hidden', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should destroy a tooltip after it is shown and hidden', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        tooltip.hide()
-      })
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        tooltip.dispose()
-        expect(tooltip.tip).toBeNull()
-        expect(Tooltip.getInstance(tooltipEl)).toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          tooltip.hide()
+        })
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          tooltip.dispose()
+          expect(tooltip.tip).toBeNull()
+          expect(Tooltip.getInstance(tooltipEl)).toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should destroy a tooltip and remove it from the dom', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should destroy a tooltip and remove it from the dom', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
 
-        tooltip.dispose()
+          tooltip.dispose()
 
-        expect(document.querySelector('.tooltip')).toBeNull()
-        done()
-      })
+          expect(document.querySelector('.tooltip')).toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
   })
 
   describe('show', () => {
-    it('should show a tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tooltipShown = document.querySelector('.tooltip')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tooltipShown = document.querySelector('.tooltip')
 
-        expect(tooltipShown).not.toBeNull()
-        expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
-        expect(tooltipShown.getAttribute('id')).toContain('tooltip')
-        done()
-      })
+          expect(tooltipShown).not.toBeNull()
+          expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
+          expect(tooltipShown.getAttribute('id')).toContain('tooltip')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip when hovering a child element', done => {
-      fixtureEl.innerHTML = [
-        '<a href="#" rel="tooltip" title="Another tooltip">',
-        '  <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">',
-        '    <rect width="100%" fill="#563d7c"/>',
-        '    <circle cx="50" cy="50" r="30" fill="#fff"/>',
-        '  </svg>',
-        '</a>'
-      ].join('')
+    it('should show a tooltip when hovering a child element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a href="#" rel="tooltip" title="Another tooltip">',
+          '  <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">',
+          '    <rect width="100%" fill="#563d7c"/>',
+          '    <circle cx="50" cy="50" r="30" fill="#fff"/>',
+          '  </svg>',
+          '</a>'
+        ].join('')
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      spyOn(tooltip, 'show')
+        spyOn(tooltip, 'show')
 
-      tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
+        tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
 
-      setTimeout(() => {
-        expect(tooltip.show).toHaveBeenCalled()
-        done()
-      }, 0)
+        setTimeout(() => {
+          expect(tooltip.show).toHaveBeenCalled()
+          resolve()
+        }, 0)
+      })
     })
 
-    it('should show a tooltip on mobile', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip on mobile', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
-      document.documentElement.ontouchstart = noop
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
+        document.documentElement.ontouchstart = noop
 
-      spyOn(EventHandler, 'on').and.callThrough()
+        spyOn(EventHandler, 'on').and.callThrough()
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
-        expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
-        document.documentElement.ontouchstart = undefined
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
+          expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+          document.documentElement.ontouchstart = undefined
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip relative to placement option', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip relative to placement option', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        placement: 'bottom'
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          placement: 'bottom'
+        })
 
-      tooltipEl.addEventListener('inserted.bs.tooltip', () => {
-        expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
-      })
+        tooltipEl.addEventListener('inserted.bs.tooltip', () => {
+          expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
-        expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
+          expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should not error when trying to show a tooltip that has been removed from the dom', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should not error when trying to show a tooltip that has been removed from the dom', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      const firstCallback = () => {
-        tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
-        let tooltipShown = document.querySelector('.tooltip')
+        const firstCallback = () => {
+          tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
+          let tooltipShown = document.querySelector('.tooltip')
 
-        tooltipShown.remove()
+          tooltipShown.remove()
 
-        tooltipEl.addEventListener('shown.bs.tooltip', () => {
-          tooltipShown = document.querySelector('.tooltip')
+          tooltipEl.addEventListener('shown.bs.tooltip', () => {
+            tooltipShown = document.querySelector('.tooltip')
 
-          expect(tooltipShown).not.toBeNull()
-          done()
-        })
+            expect(tooltipShown).not.toBeNull()
+            resolve()
+          })
 
-        tooltip.show()
-      }
+          tooltip.show()
+        }
 
-      tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
+        tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with a dom element container', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with a dom element container', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        container: fixtureEl
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          container: fixtureEl
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with a jquery element container', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with a jquery element container', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        container: {
-          0: fixtureEl,
-          jquery: 'jQuery'
-        }
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          container: {
+            0: fixtureEl,
+            jquery: 'jQuery'
+          }
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with a selector in container', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with a selector in container', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        container: '#fixture'
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          container: '#fixture'
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with placement as a function', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with placement as a function', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const spy = jasmine.createSpy('placement').and.returnValue('top')
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        placement: spy
-      })
+        const spy = jasmine.createSpy('placement').and.returnValue('top')
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          placement: spy
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).not.toBeNull()
-        expect(spy).toHaveBeenCalled()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).not.toBeNull()
+          expect(spy).toHaveBeenCalled()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip without the animation', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip without the animation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        animation: false
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          animation: false
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tip = document.querySelector('.tooltip')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tip = document.querySelector('.tooltip')
 
-        expect(tip).not.toBeNull()
-        expect(tip).not.toHaveClass('fade')
-        done()
-      })
+          expect(tip).not.toBeNull()
+          expect(tip).not.toHaveClass('fade')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
     it('should throw an error the element is not visible', () => {
@@ -615,352 +657,382 @@ describe('Tooltip', () => {
       }
     })
 
-    it('should not show a tooltip if show.bs.tooltip is prevented', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
-
-      const expectedDone = () => {
-        setTimeout(() => {
-          expect(document.querySelector('.tooltip')).toBeNull()
-          done()
-        }, 10)
-      }
+    it('should not show a tooltip if show.bs.tooltip is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      tooltipEl.addEventListener('show.bs.tooltip', ev => {
-        ev.preventDefault()
-        expectedDone()
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        throw new Error('Tooltip should not be shown')
-      })
+        const expectedDone = () => {
+          setTimeout(() => {
+            expect(document.querySelector('.tooltip')).toBeNull()
+            resolve()
+          }, 10)
+        }
 
-      tooltip.show()
-    })
+        tooltipEl.addEventListener('show.bs.tooltip', ev => {
+          ev.preventDefault()
+          expectedDone()
+        })
 
-    it('should show tooltip if leave event hasn\'t occurred before delay expires', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          throw new Error('Tooltip should not be shown')
+        })
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        delay: 150
+        tooltip.show()
       })
-
-      spyOn(tooltip, 'show')
-
-      setTimeout(() => {
-        expect(tooltip.show).not.toHaveBeenCalled()
-      }, 100)
-
-      setTimeout(() => {
-        expect(tooltip.show).toHaveBeenCalled()
-        done()
-      }, 200)
-
-      tooltipEl.dispatchEvent(createEvent('mouseover'))
     })
 
-    it('should not show tooltip if leave event occurs before delay expires', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show tooltip if leave event hasn\'t occurred before delay expires', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        delay: 150
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          delay: 150
+        })
 
-      spyOn(tooltip, 'show')
+        spyOn(tooltip, 'show')
 
-      setTimeout(() => {
-        expect(tooltip.show).not.toHaveBeenCalled()
-        tooltipEl.dispatchEvent(createEvent('mouseover'))
-      }, 100)
+        setTimeout(() => {
+          expect(tooltip.show).not.toHaveBeenCalled()
+        }, 100)
 
-      setTimeout(() => {
-        expect(tooltip.show).toHaveBeenCalled()
-        expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
-        done()
-      }, 200)
+        setTimeout(() => {
+          expect(tooltip.show).toHaveBeenCalled()
+          resolve()
+        }, 200)
 
-      tooltipEl.dispatchEvent(createEvent('mouseover'))
+        tooltipEl.dispatchEvent(createEvent('mouseover'))
+      })
     })
 
-    it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should not show tooltip if leave event occurs before delay expires', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        delay: {
-          show: 0,
-          hide: 150
-        }
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          delay: 150
+        })
 
-      setTimeout(() => {
-        expect(tooltip._getTipElement()).toHaveClass('show')
-        tooltipEl.dispatchEvent(createEvent('mouseout'))
+        spyOn(tooltip, 'show')
 
         setTimeout(() => {
-          expect(tooltip._getTipElement()).toHaveClass('show')
+          expect(tooltip.show).not.toHaveBeenCalled()
           tooltipEl.dispatchEvent(createEvent('mouseover'))
         }, 100)
 
         setTimeout(() => {
-          expect(tooltip._getTipElement()).toHaveClass('show')
-          expect(document.querySelectorAll('.tooltip')).toHaveSize(1)
-          done()
+          expect(tooltip.show).toHaveBeenCalled()
+          expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
+          resolve()
         }, 200)
-      }, 10)
 
-      tooltipEl.dispatchEvent(createEvent('mouseover'))
+        tooltipEl.dispatchEvent(createEvent('mouseover'))
+      })
     })
 
-    it('should not hide tooltip if leave event occurs and interaction remains inside trigger', done => {
-      fixtureEl.innerHTML = [
-        '<a href="#" rel="tooltip" title="Another tooltip">',
-        '<b>Trigger</b>',
-        'the tooltip',
-        '</a>'
-      ].join('')
-
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
-      const triggerChild = tooltipEl.querySelector('b')
-
-      spyOn(tooltip, 'hide').and.callThrough()
+    it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      tooltipEl.addEventListener('mouseover', () => {
-        const moveMouseToChildEvent = createEvent('mouseout')
-        Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
-          value: triggerChild
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          delay: {
+            show: 0,
+            hide: 150
+          }
         })
 
-        tooltipEl.dispatchEvent(moveMouseToChildEvent)
-      })
+        setTimeout(() => {
+          expect(tooltip._getTipElement()).toHaveClass('show')
+          tooltipEl.dispatchEvent(createEvent('mouseout'))
+
+          setTimeout(() => {
+            expect(tooltip._getTipElement()).toHaveClass('show')
+            tooltipEl.dispatchEvent(createEvent('mouseover'))
+          }, 100)
+
+          setTimeout(() => {
+            expect(tooltip._getTipElement()).toHaveClass('show')
+            expect(document.querySelectorAll('.tooltip')).toHaveSize(1)
+            resolve()
+          }, 200)
+        }, 10)
 
-      tooltipEl.addEventListener('mouseout', () => {
-        expect(tooltip.hide).not.toHaveBeenCalled()
-        done()
+        tooltipEl.dispatchEvent(createEvent('mouseover'))
       })
-
-      tooltipEl.dispatchEvent(createEvent('mouseover'))
     })
 
-    it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', done => {
-      // Style this tooltip to give it plenty of room for popper to do what it wants
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
+    it('should not hide tooltip if leave event occurs and interaction remains inside trigger', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = [
+          '<a href="#" rel="tooltip" title="Another tooltip">',
+          '<b>Trigger</b>',
+          'the tooltip',
+          '</a>'
+        ].join('')
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
+        const triggerChild = tooltipEl.querySelector('b')
+
+        spyOn(tooltip, 'hide').and.callThrough()
+
+        tooltipEl.addEventListener('mouseover', () => {
+          const moveMouseToChildEvent = createEvent('mouseout')
+          Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
+            value: triggerChild
+          })
+
+          tooltipEl.dispatchEvent(moveMouseToChildEvent)
+        })
 
-      spyOn(window, 'getComputedStyle').and.returnValue({
-        transitionDuration: '0.15s',
-        transitionDelay: '0s'
+        tooltipEl.addEventListener('mouseout', () => {
+          expect(tooltip.hide).not.toHaveBeenCalled()
+          resolve()
+        })
+
+        tooltipEl.dispatchEvent(createEvent('mouseover'))
       })
+    })
 
-      setTimeout(() => {
-        expect(tooltip._popper).not.toBeNull()
-        expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
-        tooltipEl.dispatchEvent(createEvent('mouseout'))
+    it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', () => {
+      return new Promise(resolve => {
+        // Style this tooltip to give it plenty of room for popper to do what it wants
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
 
-        setTimeout(() => {
-          expect(tooltip._getTipElement()).not.toHaveClass('show')
-          tooltipEl.dispatchEvent(createEvent('mouseover'))
-        }, 100)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
+
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.15s',
+          transitionDelay: '0s'
+        })
 
         setTimeout(() => {
           expect(tooltip._popper).not.toBeNull()
           expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
-          done()
-        }, 200)
-      }, 10)
+          tooltipEl.dispatchEvent(createEvent('mouseout'))
+
+          setTimeout(() => {
+            expect(tooltip._getTipElement()).not.toHaveClass('show')
+            tooltipEl.dispatchEvent(createEvent('mouseover'))
+          }, 100)
+
+          setTimeout(() => {
+            expect(tooltip._popper).not.toBeNull()
+            expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
+            resolve()
+          }, 200)
+        }, 10)
 
-      tooltipEl.dispatchEvent(createEvent('mouseover'))
+        tooltipEl.dispatchEvent(createEvent('mouseover'))
+      })
     })
 
-    it('should only trigger inserted event if a new tooltip element was created', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+    it('should only trigger inserted event if a new tooltip element was created', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      spyOn(window, 'getComputedStyle').and.returnValue({
-        transitionDuration: '0.15s',
-        transitionDelay: '0s'
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      const insertedFunc = jasmine.createSpy()
-      tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
-
-      setTimeout(() => {
-        expect(insertedFunc).toHaveBeenCalledTimes(1)
-        tooltip.hide()
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.15s',
+          transitionDelay: '0s'
+        })
 
-        setTimeout(() => {
-          tooltip.show()
-        }, 100)
+        const insertedFunc = jasmine.createSpy()
+        tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
 
         setTimeout(() => {
           expect(insertedFunc).toHaveBeenCalledTimes(1)
-          done()
-        }, 200)
-      }, 0)
+          tooltip.hide()
 
-      tooltip.show()
+          setTimeout(() => {
+            tooltip.show()
+          }, 100)
+
+          setTimeout(() => {
+            expect(insertedFunc).toHaveBeenCalledTimes(1)
+            resolve()
+          }, 200)
+        }, 0)
+
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with custom class provided in data attributes', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
+    it('should show a tooltip with custom class provided in data attributes', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tip = document.querySelector('.tooltip')
-        expect(tip).not.toBeNull()
-        expect(tip).toHaveClass('custom-class')
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tip = document.querySelector('.tooltip')
+          expect(tip).not.toBeNull()
+          expect(tip).toHaveClass('custom-class')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with custom class provided as a string in config', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with custom class provided as a string in config', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        customClass: 'custom-class custom-class-2'
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          customClass: 'custom-class custom-class-2'
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tip = document.querySelector('.tooltip')
-        expect(tip).not.toBeNull()
-        expect(tip).toHaveClass('custom-class')
-        expect(tip).toHaveClass('custom-class-2')
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tip = document.querySelector('.tooltip')
+          expect(tip).not.toBeNull()
+          expect(tip).toHaveClass('custom-class')
+          expect(tip).toHaveClass('custom-class-2')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should show a tooltip with custom class provided as a function in config', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should show a tooltip with custom class provided as a function in config', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        customClass: spy
-      })
+        const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          customClass: spy
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tip = document.querySelector('.tooltip')
-        expect(tip).not.toBeNull()
-        expect(spy).toHaveBeenCalled()
-        expect(tip).toHaveClass('custom-class')
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tip = document.querySelector('.tooltip')
+          expect(tip).not.toBeNull()
+          expect(spy).toHaveBeenCalled()
+          expect(tip).toHaveClass('custom-class')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should remove `title` attribute if exists', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+    it('should remove `title` attribute if exists', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(tooltipEl.getAttribute('title')).toBeNull()
-        done()
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          expect(tooltipEl.getAttribute('title')).toBeNull()
+          resolve()
+        })
+        tooltip.show()
       })
-      tooltip.show()
     })
   })
 
   describe('hide', () => {
-    it('should hide a tooltip', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should hide a tooltip', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).toBeNull()
-        expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).toBeNull()
+          expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should hide a tooltip on mobile', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should hide a tooltip on mobile', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        document.documentElement.ontouchstart = noop
-        spyOn(EventHandler, 'off')
-        tooltip.hide()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          document.documentElement.ontouchstart = noop
+          spyOn(EventHandler, 'off')
+          tooltip.hide()
+        })
 
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).toBeNull()
-        expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
-        document.documentElement.ontouchstart = undefined
-        done()
-      })
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).toBeNull()
+          expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+          document.documentElement.ontouchstart = undefined
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should hide a tooltip without animation', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should hide a tooltip without animation', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        animation: false
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          animation: false
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).toBeNull()
-        expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
-        done()
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          expect(document.querySelector('.tooltip')).toBeNull()
+          expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should not hide a tooltip if hide event is prevented', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should not hide a tooltip if hide event is prevented', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const assertDone = () => {
-        setTimeout(() => {
-          expect(document.querySelector('.tooltip')).not.toBeNull()
-          done()
-        }, 20)
-      }
+        const assertDone = () => {
+          setTimeout(() => {
+            expect(document.querySelector('.tooltip')).not.toBeNull()
+            resolve()
+          }, 20)
+        }
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        animation: false
-      })
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl, {
+          animation: false
+        })
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
-      tooltipEl.addEventListener('hide.bs.tooltip', event => {
-        event.preventDefault()
-        assertDone()
-      })
-      tooltipEl.addEventListener('hidden.bs.tooltip', () => {
-        throw new Error('should not trigger hidden event')
-      })
+        tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+        tooltipEl.addEventListener('hide.bs.tooltip', event => {
+          event.preventDefault()
+          assertDone()
+        })
+        tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+          throw new Error('should not trigger hidden event')
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
     it('should not throw error running hide if popper hasn\'t been shown', () => {
@@ -979,22 +1051,24 @@ describe('Tooltip', () => {
   })
 
   describe('update', () => {
-    it('should call popper update', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+    it('should call popper update', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        spyOn(tooltip._popper, 'update')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          spyOn(tooltip._popper, 'update')
 
-        tooltip.update()
+          tooltip.update()
 
-        expect(tooltip._popper.update).toHaveBeenCalled()
-        done()
-      })
+          expect(tooltip._popper.update).toHaveBeenCalled()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
     it('should do nothing if the tooltip is not shown', () => {
@@ -1272,55 +1346,61 @@ describe('Tooltip', () => {
   })
 
   describe('aria-label', () => {
-    it('should add the aria-label attribute for referencing original title', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+    it('should add the aria-label attribute for referencing original title', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tooltipShown = document.querySelector('.tooltip')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tooltipShown = document.querySelector('.tooltip')
 
-        expect(tooltipShown).not.toBeNull()
-        expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
-        done()
-      })
+          expect(tooltipShown).not.toBeNull()
+          expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should not add the aria-label attribute if the attribute already exists', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
+    it('should not add the aria-label attribute if the attribute already exists', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tooltipShown = document.querySelector('.tooltip')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tooltipShown = document.querySelector('.tooltip')
 
-        expect(tooltipShown).not.toBeNull()
-        expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
-        done()
-      })
+          expect(tooltipShown).not.toBeNull()
+          expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
 
-    it('should not add the aria-label attribute if the element has text content', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
+    it('should not add the aria-label attribute if the element has text content', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
 
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl)
+        const tooltipEl = fixtureEl.querySelector('a')
+        const tooltip = new Tooltip(tooltipEl)
 
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        const tooltipShown = document.querySelector('.tooltip')
+        tooltipEl.addEventListener('shown.bs.tooltip', () => {
+          const tooltipShown = document.querySelector('.tooltip')
 
-        expect(tooltipShown).not.toBeNull()
-        expect(tooltipEl.getAttribute('aria-label')).toBeNull()
-        done()
-      })
+          expect(tooltipShown).not.toBeNull()
+          expect(tooltipEl.getAttribute('aria-label')).toBeNull()
+          resolve()
+        })
 
-      tooltip.show()
+        tooltip.show()
+      })
     })
   })
 
index f9903c8327ac8bbb6c0ef0b171e33e26f744f60b..73384fc90b6d8b455ebe962555a8aa00d6485ea3 100644 (file)
@@ -23,267 +23,297 @@ describe('Backdrop', () => {
   })
 
   describe('show', () => {
-    it('should append the backdrop html once on show and include the "show" class if it is "shown"', done => {
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: false
-      })
-      const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+    it('should append the backdrop html once on show and include the "show" class if it is "shown"', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: false
+        })
+        const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
 
-      expect(getElements()).toHaveSize(0)
+        expect(getElements()).toHaveSize(0)
 
-      instance.show()
-      instance.show(() => {
-        expect(getElements()).toHaveSize(1)
-        for (const el of getElements()) {
-          expect(el).toHaveClass(CLASS_NAME_SHOW)
-        }
+        instance.show()
+        instance.show(() => {
+          expect(getElements()).toHaveSize(1)
+          for (const el of getElements()) {
+            expect(el).toHaveClass(CLASS_NAME_SHOW)
+          }
 
-        done()
+          resolve()
+        })
       })
     })
 
-    it('should not append the backdrop html if it is not "shown"', done => {
-      const instance = new Backdrop({
-        isVisible: false,
-        isAnimated: true
-      })
-      const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+    it('should not append the backdrop html if it is not "shown"', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: false,
+          isAnimated: true
+        })
+        const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
 
-      expect(getElements()).toHaveSize(0)
-      instance.show(() => {
         expect(getElements()).toHaveSize(0)
-        done()
+        instance.show(() => {
+          expect(getElements()).toHaveSize(0)
+          resolve()
+        })
       })
     })
 
-    it('should append the backdrop html once and include the "fade" class if it is "shown" and "animated"', done => {
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: true
-      })
-      const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+    it('should append the backdrop html once and include the "fade" class if it is "shown" and "animated"', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: true
+        })
+        const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
 
-      expect(getElements()).toHaveSize(0)
+        expect(getElements()).toHaveSize(0)
 
-      instance.show(() => {
-        expect(getElements()).toHaveSize(1)
-        for (const el of getElements()) {
-          expect(el).toHaveClass(CLASS_NAME_FADE)
-        }
+        instance.show(() => {
+          expect(getElements()).toHaveSize(1)
+          for (const el of getElements()) {
+            expect(el).toHaveClass(CLASS_NAME_FADE)
+          }
 
-        done()
+          resolve()
+        })
       })
     })
   })
 
   describe('hide', () => {
-    it('should remove the backdrop html', done => {
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: true
-      })
+    it('should remove the backdrop html', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: true
+        })
 
-      const getElements = () => document.body.querySelectorAll(CLASS_BACKDROP)
+        const getElements = () => document.body.querySelectorAll(CLASS_BACKDROP)
 
-      expect(getElements()).toHaveSize(0)
-      instance.show(() => {
-        expect(getElements()).toHaveSize(1)
-        instance.hide(() => {
-          expect(getElements()).toHaveSize(0)
-          done()
+        expect(getElements()).toHaveSize(0)
+        instance.show(() => {
+          expect(getElements()).toHaveSize(1)
+          instance.hide(() => {
+            expect(getElements()).toHaveSize(0)
+            resolve()
+          })
         })
       })
     })
 
-    it('should remove the "show" class', done => {
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: true
-      })
-      const elem = instance._getElement()
+    it('should remove the "show" class', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: true
+        })
+        const elem = instance._getElement()
 
-      instance.show()
-      instance.hide(() => {
-        expect(elem).not.toHaveClass(CLASS_NAME_SHOW)
-        done()
+        instance.show()
+        instance.hide(() => {
+          expect(elem).not.toHaveClass(CLASS_NAME_SHOW)
+          resolve()
+        })
       })
     })
 
-    it('should not try to remove Node on remove method if it is not "shown"', done => {
-      const instance = new Backdrop({
-        isVisible: false,
-        isAnimated: true
-      })
-      const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
-      const spy = spyOn(instance, 'dispose').and.callThrough()
+    it('should not try to remove Node on remove method if it is not "shown"', () => {
+      return new Promise(resolve => {
+        const instance = new Backdrop({
+          isVisible: false,
+          isAnimated: true
+        })
+        const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+        const spy = spyOn(instance, 'dispose').and.callThrough()
 
-      expect(getElements()).toHaveSize(0)
-      expect(instance._isAppended).toBeFalse()
-      instance.show(() => {
-        instance.hide(() => {
-          expect(getElements()).toHaveSize(0)
-          expect(spy).not.toHaveBeenCalled()
-          expect(instance._isAppended).toBeFalse()
-          done()
+        expect(getElements()).toHaveSize(0)
+        expect(instance._isAppended).toBeFalse()
+        instance.show(() => {
+          instance.hide(() => {
+            expect(getElements()).toHaveSize(0)
+            expect(spy).not.toHaveBeenCalled()
+            expect(instance._isAppended).toBeFalse()
+            resolve()
+          })
         })
       })
     })
 
-    it('should not error if the backdrop no longer has a parent', done => {
-      fixtureEl.innerHTML = '<div id="wrapper"></div>'
+    it('should not error if the backdrop no longer has a parent', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div id="wrapper"></div>'
 
-      const wrapper = fixtureEl.querySelector('#wrapper')
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: true,
-        rootElement: wrapper
-      })
+        const wrapper = fixtureEl.querySelector('#wrapper')
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: true,
+          rootElement: wrapper
+        })
 
-      const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+        const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
 
-      instance.show(() => {
-        wrapper.remove()
-        instance.hide(() => {
-          expect(getElements()).toHaveSize(0)
-          done()
+        instance.show(() => {
+          wrapper.remove()
+          instance.hide(() => {
+            expect(getElements()).toHaveSize(0)
+            resolve()
+          })
         })
       })
     })
   })
 
   describe('click callback', () => {
-    it('should execute callback on click', done => {
-      const spy = jasmine.createSpy('spy')
+    it('should execute callback on click', () => {
+      return new Promise(resolve => {
+        const spy = jasmine.createSpy('spy')
 
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: false,
-        clickCallback: () => spy()
-      })
-      const endTest = () => {
-        setTimeout(() => {
-          expect(spy).toHaveBeenCalled()
-          done()
-        }, 10)
-      }
-
-      instance.show(() => {
-        const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
-        document.querySelector(CLASS_BACKDROP).dispatchEvent(clickEvent)
-        endTest()
-      })
-    })
-  })
+        const instance = new Backdrop({
+          isVisible: true,
+          isAnimated: false,
+          clickCallback: () => spy()
+        })
+        const endTest = () => {
+          setTimeout(() => {
+            expect(spy).toHaveBeenCalled()
+            resolve()
+          }, 10)
+        }
 
-  describe('animation callbacks', () => {
-    it('should show and hide backdrop after counting transition duration if it is animated', done => {
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: true
-      })
-      const spy2 = jasmine.createSpy('spy2')
-
-      const execDone = () => {
-        setTimeout(() => {
-          expect(spy2).toHaveBeenCalledTimes(2)
-          done()
-        }, 10)
-      }
-
-      instance.show(spy2)
-      instance.hide(() => {
-        spy2()
-        execDone()
+        instance.show(() => {
+          const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
+          document.querySelector(CLASS_BACKDROP).dispatchEvent(clickEvent)
+          endTest()
+        })
       })
-      expect(spy2).not.toHaveBeenCalled()
     })
 
-    it('should show and hide backdrop without a delay if it is not animated', done => {
-      const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
-      const instance = new Backdrop({
-        isVisible: true,
-        isAnimated: false
+    describe('animation callbacks', () => {
+      it('should show and hide backdrop after counting transition duration if it is animated', () => {
+        return new Promise(resolve => {
+          const instance = new Backdrop({
+            isVisible: true,
+            isAnimated: true
+          })
+          const spy2 = jasmine.createSpy('spy2')
+
+          const execDone = () => {
+            setTimeout(() => {
+              expect(spy2).toHaveBeenCalledTimes(2)
+              resolve()
+            }, 10)
+          }
+
+          instance.show(spy2)
+          instance.hide(() => {
+            spy2()
+            execDone()
+          })
+          expect(spy2).not.toHaveBeenCalled()
+        })
       })
-      const spy2 = jasmine.createSpy('spy2')
 
-      instance.show(spy2)
-      instance.hide(spy2)
-
-      setTimeout(() => {
-        expect(spy2).toHaveBeenCalled()
-        expect(spy).not.toHaveBeenCalled()
-        done()
-      }, 10)
-    })
-
-    it('should not call delay callbacks if it is not "shown"', done => {
-      const instance = new Backdrop({
-        isVisible: false,
-        isAnimated: true
+      it('should show and hide backdrop without a delay if it is not animated', () => {
+        return new Promise(resolve => {
+          const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+          const instance = new Backdrop({
+            isVisible: true,
+            isAnimated: false
+          })
+          const spy2 = jasmine.createSpy('spy2')
+
+          instance.show(spy2)
+          instance.hide(spy2)
+
+          setTimeout(() => {
+            expect(spy2).toHaveBeenCalled()
+            expect(spy).not.toHaveBeenCalled()
+            resolve()
+          }, 10)
+        })
       })
-      const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
 
-      instance.show()
-      instance.hide(() => {
-        expect(spy).not.toHaveBeenCalled()
-        done()
+      it('should not call delay callbacks if it is not "shown"', () => {
+        return new Promise(resolve => {
+          const instance = new Backdrop({
+            isVisible: false,
+            isAnimated: true
+          })
+          const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+
+          instance.show()
+          instance.hide(() => {
+            expect(spy).not.toHaveBeenCalled()
+            resolve()
+          })
+        })
       })
     })
-  })
 
-  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()
+    describe('Config', () => {
+      describe('rootElement initialization', () => {
+        it('should be appended on "document.body" by default', () => {
+          return new Promise(resolve => {
+            const instance = new Backdrop({
+              isVisible: true
+            })
+            const getElement = () => document.querySelector(CLASS_BACKDROP)
+            instance.show(() => {
+              expect(getElement().parentElement).toEqual(document.body)
+              resolve()
+            })
+          })
         })
-      })
 
-      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', () => {
+          return new Promise(resolve => {
+            const instance = new Backdrop({
+              isVisible: true,
+              rootElement: 'body'
+            })
+            const getElement = () => document.querySelector(CLASS_BACKDROP)
+            instance.show(() => {
+              expect(getElement().parentElement).toEqual(document.body)
+              resolve()
+            })
+          })
         })
-      })
-
-      it('should be appended on any element given by the proper config', done => {
-        fixtureEl.innerHTML = '<div id="wrapper"></div>'
 
-        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()
+        it('should be appended on any element given by the proper config', () => {
+          return new Promise(resolve => {
+            fixtureEl.innerHTML = '<div id="wrapper"></div>'
+
+            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)
+              resolve()
+            })
+          })
         })
       })
-    })
 
-    describe('ClassName', () => {
-      it('should allow configuring className', done => {
-        const instance = new Backdrop({
-          isVisible: true,
-          className: 'foo'
-        })
-        const getElement = () => document.querySelector('.foo')
-        instance.show(() => {
-          expect(getElement()).toEqual(instance._getElement())
-          instance.dispose()
-          done()
+      describe('ClassName', () => {
+        it('should allow configuring className', () => {
+          return new Promise(resolve => {
+            const instance = new Backdrop({
+              isVisible: true,
+              className: 'foo'
+            })
+            const getElement = () => document.querySelector('.foo')
+            instance.show(() => {
+              expect(getElement()).toEqual(instance._getElement())
+              instance.dispose()
+              resolve()
+            })
+          })
         })
       })
     })
index 52a7573971c21884e8a70ff05ac429374a745ca7..55f6a23a7ec7a4ce364baf89a4723249fcd8ca56 100644 (file)
@@ -1,7 +1,7 @@
 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'
+import { clearFixture, createEvent, getFixture } from '../../helpers/fixture'
 
 describe('FocusTrap', () => {
   let fixtureEl
@@ -41,140 +41,148 @@ describe('FocusTrap', () => {
       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('')
+    it('should force focus inside focus trap if it can', () => {
+      return new Promise(resolve => {
+        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 trapElement = fixtureEl.querySelector('div')
+        const focustrap = new FocusTrap({ trapElement })
+        focustrap.activate()
 
-      const inside = document.getElementById('inside')
+        const inside = document.getElementById('inside')
 
-      const focusInListener = () => {
-        expect(inside.focus).toHaveBeenCalled()
-        document.removeEventListener('focusin', focusInListener)
-        done()
-      }
+        const focusInListener = () => {
+          expect(inside.focus).toHaveBeenCalled()
+          document.removeEventListener('focusin', focusInListener)
+          resolve()
+        }
 
-      spyOn(inside, 'focus')
-      spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside])
+        spyOn(inside, 'focus')
+        spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside])
 
-      document.addEventListener('focusin', focusInListener)
+        document.addEventListener('focusin', focusInListener)
 
-      const focusInEvent = createEvent('focusin', { bubbles: true })
-      Object.defineProperty(focusInEvent, 'target', {
-        value: document.getElementById('outside')
-      })
+        const focusInEvent = createEvent('focusin', { bubbles: true })
+        Object.defineProperty(focusInEvent, 'target', {
+          value: document.getElementById('outside')
+        })
 
-      document.dispatchEvent(focusInEvent)
+        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 forward on tab', () => {
+      return new Promise(resolve => {
+        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)
+          resolve()
+        }
+
+        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 wrap focus around backwards on shift-tab', () => {
+      return new Promise(resolve => {
+        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)
+          resolve()
+        }
+
+        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('')
+    it('should force focus on itself if there is no focusable content', () => {
+      return new Promise(resolve => {
+        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 trapElement = fixtureEl.querySelector('div')
+        const focustrap = new FocusTrap({ trapElement })
+        focustrap.activate()
 
-      const focusInListener = () => {
-        expect(focustrap._config.trapElement.focus).toHaveBeenCalled()
-        document.removeEventListener('focusin', focusInListener)
-        done()
-      }
+        const focusInListener = () => {
+          expect(focustrap._config.trapElement.focus).toHaveBeenCalled()
+          document.removeEventListener('focusin', focusInListener)
+          resolve()
+        }
 
-      spyOn(focustrap._config.trapElement, 'focus')
+        spyOn(focustrap._config.trapElement, 'focus')
 
-      document.addEventListener('focusin', focusInListener)
+        document.addEventListener('focusin', focusInListener)
 
-      const focusInEvent = createEvent('focusin', { bubbles: true })
-      Object.defineProperty(focusInEvent, 'target', {
-        value: document.getElementById('outside')
-      })
+        const focusInEvent = createEvent('focusin', { bubbles: true })
+        Object.defineProperty(focusInEvent, 'target', {
+          value: document.getElementById('outside')
+        })
 
-      document.dispatchEvent(focusInEvent)
+        document.dispatchEvent(focusInEvent)
+      })
     })
   })
 
index 9d8c5ed98627b28ccd259b35a21f5886d8ca6209..0d60ca989469a1d762239eb94755c05528c76997 100644 (file)
@@ -1,5 +1,6 @@
 import * as Util from '../../../src/util/index'
 import { clearFixture, getFixture } from '../../helpers/fixture'
+import { noop } from '../../../src/util/index'
 
 describe('Util', () => {
   let fixtureEl
@@ -154,18 +155,20 @@ describe('Util', () => {
   })
 
   describe('triggerTransitionEnd', () => {
-    it('should trigger transitionend event', done => {
-      fixtureEl.innerHTML = '<div></div>'
+    it('should trigger transitionend event', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<div></div>'
 
-      const el = fixtureEl.querySelector('div')
-      const spy = spyOn(el, 'dispatchEvent').and.callThrough()
+        const el = fixtureEl.querySelector('div')
+        const spy = spyOn(el, 'dispatchEvent').and.callThrough()
 
-      el.addEventListener('transitionend', () => {
-        expect(spy).toHaveBeenCalled()
-        done()
-      })
+        el.addEventListener('transitionend', () => {
+          expect(spy).toHaveBeenCalled()
+          resolve()
+        })
 
-      Util.triggerTransitionEnd(el)
+        Util.triggerTransitionEnd(el)
+      })
     })
   })
 
@@ -611,9 +614,9 @@ describe('Util', () => {
     })
 
     it('should define a plugin on the jQuery instance', () => {
-      const pluginMock = function () {}
+      const pluginMock = Util.noop
       pluginMock.NAME = 'test'
-      pluginMock.jQueryInterface = function () {}
+      pluginMock.jQueryInterface = Util.noop
 
       Util.defineJQueryPlugin(pluginMock)
       expect(fakejQuery.fn.test).toEqual(pluginMock.jQueryInterface)
@@ -658,96 +661,104 @@ describe('Util', () => {
       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')
+    it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', () => {
+      return new Promise(resolve => {
+        const el = document.createElement('div')
+        const callbackSpy = jasmine.createSpy('callback spy')
 
-      spyOn(window, 'getComputedStyle').and.returnValue({
-        transitionDuration: '0.05s',
-        transitionDelay: '0s'
-      })
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.05s',
+          transitionDelay: '0s'
+        })
 
-      Util.executeAfterTransition(callbackSpy, el)
+        Util.executeAfterTransition(callbackSpy, el)
 
-      setTimeout(() => {
-        expect(callbackSpy).toHaveBeenCalled()
-        done()
-      }, 70)
+        setTimeout(() => {
+          expect(callbackSpy).toHaveBeenCalled()
+          resolve()
+        }, 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')
+    it('should not execute a function a second time after a computed CSS transition duration and if a transitionend event has already been dispatched', () => {
+      return new Promise(resolve => {
+        const el = document.createElement('div')
+        const callbackSpy = jasmine.createSpy('callback spy')
 
-      spyOn(window, 'getComputedStyle').and.returnValue({
-        transitionDuration: '0.05s',
-        transitionDelay: '0s'
-      })
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.05s',
+          transitionDelay: '0s'
+        })
 
-      Util.executeAfterTransition(callbackSpy, el)
+        Util.executeAfterTransition(callbackSpy, el)
 
-      setTimeout(() => {
-        el.dispatchEvent(new TransitionEvent('transitionend'))
-      }, 50)
+        setTimeout(() => {
+          el.dispatchEvent(new TransitionEvent('transitionend'))
+        }, 50)
 
-      setTimeout(() => {
-        expect(callbackSpy).toHaveBeenCalledTimes(1)
-        done()
-      }, 70)
+        setTimeout(() => {
+          expect(callbackSpy).toHaveBeenCalledTimes(1)
+          resolve()
+        }, 70)
+      })
     })
 
-    it('should not trigger a transitionend event if another transitionend event had already happened', done => {
-      const el = document.createElement('div')
+    it('should not trigger a transitionend event if another transitionend event had already happened', () => {
+      return new Promise(resolve => {
+        const el = document.createElement('div')
 
-      spyOn(window, 'getComputedStyle').and.returnValue({
-        transitionDuration: '0.05s',
-        transitionDelay: '0s'
-      })
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.05s',
+          transitionDelay: '0s'
+        })
 
-      Util.executeAfterTransition(() => {}, el)
+        Util.executeAfterTransition(noop, el)
 
-      // simulate a event dispatched by the browser
-      el.dispatchEvent(new TransitionEvent('transitionend'))
+        // simulate a event dispatched by the browser
+        el.dispatchEvent(new TransitionEvent('transitionend'))
 
-      const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
+        const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
 
-      setTimeout(() => {
-        // setTimeout should not have triggered another transitionend event.
-        expect(dispatchSpy).not.toHaveBeenCalled()
-        done()
-      }, 70)
+        setTimeout(() => {
+          // setTimeout should not have triggered another transitionend event.
+          expect(dispatchSpy).not.toHaveBeenCalled()
+          resolve()
+        }, 70)
+      })
     })
 
-    it('should ignore transitionend events from nested elements', done => {
-      fixtureEl.innerHTML = [
-        '<div class="outer">',
-        '  <div class="nested"></div>',
-        '</div>'
-      ].join('')
+    it('should ignore transitionend events from nested elements', () => {
+      return new Promise(resolve => {
+        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')
+        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'
-      })
+        spyOn(window, 'getComputedStyle').and.returnValue({
+          transitionDuration: '0.05s',
+          transitionDelay: '0s'
+        })
 
-      Util.executeAfterTransition(callbackSpy, outer)
+        Util.executeAfterTransition(callbackSpy, outer)
 
-      nested.dispatchEvent(new TransitionEvent('transitionend', {
-        bubbles: true
-      }))
+        nested.dispatchEvent(new TransitionEvent('transitionend', {
+          bubbles: true
+        }))
 
-      setTimeout(() => {
-        expect(callbackSpy).not.toHaveBeenCalled()
-      }, 20)
+        setTimeout(() => {
+          expect(callbackSpy).not.toHaveBeenCalled()
+        }, 20)
 
-      setTimeout(() => {
-        expect(callbackSpy).toHaveBeenCalled()
-        done()
-      }, 70)
+        setTimeout(() => {
+          expect(callbackSpy).toHaveBeenCalled()
+          resolve()
+        }, 70)
+      })
     })
   })
 
index fc3a6f4e8773e191e9307566bd84cd358e6def2e..6fcf5718bb4fc338e0da0a1a64e10c5e6c3f221d 100644 (file)
@@ -101,7 +101,7 @@ describe('ScrollBar', () => {
   })
 
   describe('hide - reset', () => {
-    it('should adjust the inline padding of fixed elements which are full-width', done => {
+    it('should adjust the inline padding of fixed elements which are full-width', () => {
       fixtureEl.innerHTML = [
         '<div style="height: 110vh; width: 100%">',
         '  <div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>',
@@ -134,10 +134,9 @@ describe('ScrollBar', () => {
       expect(getPaddingAttr(fixedEl2)).toBeNull()
       expect(currentPadding).toEqual(originalPadding)
       expect(currentPadding2).toEqual(originalPadding2)
-      done()
     })
 
-    it('should remove padding & margin if not existed before adjustment', done => {
+    it('should remove padding & margin if not existed before adjustment', () => {
       fixtureEl.innerHTML = [
         '<div style="height: 110vh; width: 100%">',
         '  <div class="fixed" id="fixed" style="width: 100vw;"></div>',
@@ -155,10 +154,9 @@ describe('ScrollBar', () => {
 
       expect(fixedEl.getAttribute('style').includes('padding-right')).toBeFalse()
       expect(stickyEl.getAttribute('style').includes('margin-right')).toBeFalse()
-      done()
     })
 
-    it('should adjust the inline margin and padding of sticky elements', done => {
+    it('should adjust the inline margin and padding of sticky elements', () => {
       fixtureEl.innerHTML = [
         '<div style="height: 110vh">',
         '  <div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>',
@@ -184,7 +182,6 @@ describe('ScrollBar', () => {
       expect(getMarginX(stickyTopEl)).toEqual(originalMargin)
       expect(getPaddingAttr(stickyTopEl)).toBeNull()
       expect(getPaddingX(stickyTopEl)).toEqual(originalPadding)
-      done()
     })
 
     it('should not adjust the inline margin and padding of sticky and fixed elements when element do not have full width', () => {
index 93131b8fddd380ac7fa1bdc28cd18618382b3c19..054aab6958fa108bdfc1cb4ffa90a39b6748352b 100644 (file)
@@ -78,74 +78,80 @@ describe('Swipe', () => {
   })
 
   describe('Config', () => {
-    it('Test leftCallback', done => {
-      const spyRight = jasmine.createSpy('spy')
-      clearPointerEvents()
-      defineDocumentElementOntouchstart()
-      // eslint-disable-next-line no-new
-      new Swipe(swipeEl, {
-        leftCallback: () => {
-          expect(spyRight).not.toHaveBeenCalled()
-          restorePointerEvents()
-          done()
-        },
-        rightCallback: spyRight
-      })
-
-      mockSwipeGesture(swipeEl, {
-        pos: [300, 10],
-        deltaX: -300
+    it('Test leftCallback', () => {
+      return new Promise(resolve => {
+        const spyRight = jasmine.createSpy('spy')
+        clearPointerEvents()
+        defineDocumentElementOntouchstart()
+        // eslint-disable-next-line no-new
+        new Swipe(swipeEl, {
+          leftCallback: () => {
+            expect(spyRight).not.toHaveBeenCalled()
+            restorePointerEvents()
+            resolve()
+          },
+          rightCallback: spyRight
+        })
+
+        mockSwipeGesture(swipeEl, {
+          pos: [300, 10],
+          deltaX: -300
+        })
       })
     })
 
-    it('Test rightCallback', done => {
-      const spyLeft = jasmine.createSpy('spy')
-      clearPointerEvents()
-      defineDocumentElementOntouchstart()
-      // eslint-disable-next-line no-new
-      new Swipe(swipeEl, {
-        rightCallback: () => {
-          expect(spyLeft).not.toHaveBeenCalled()
-          restorePointerEvents()
-          done()
-        },
-        leftCallback: spyLeft
-      })
-
-      mockSwipeGesture(swipeEl, {
-        pos: [10, 10],
-        deltaX: 300
+    it('Test rightCallback', () => {
+      return new Promise(resolve => {
+        const spyLeft = jasmine.createSpy('spy')
+        clearPointerEvents()
+        defineDocumentElementOntouchstart()
+        // eslint-disable-next-line no-new
+        new Swipe(swipeEl, {
+          rightCallback: () => {
+            expect(spyLeft).not.toHaveBeenCalled()
+            restorePointerEvents()
+            resolve()
+          },
+          leftCallback: spyLeft
+        })
+
+        mockSwipeGesture(swipeEl, {
+          pos: [10, 10],
+          deltaX: 300
+        })
       })
     })
 
-    it('Test endCallback', done => {
-      clearPointerEvents()
-      defineDocumentElementOntouchstart()
-      let isFirstTime = true
-
-      const callback = () => {
-        if (isFirstTime) {
-          isFirstTime = false
-          return
-        }
+    it('Test endCallback', () => {
+      return new Promise(resolve => {
+        clearPointerEvents()
+        defineDocumentElementOntouchstart()
+        let isFirstTime = true
 
-        expect().nothing()
-        restorePointerEvents()
-        done()
-      }
+        const callback = () => {
+          if (isFirstTime) {
+            isFirstTime = false
+            return
+          }
 
-      // eslint-disable-next-line no-new
-      new Swipe(swipeEl, {
-        endCallback: callback
-      })
-      mockSwipeGesture(swipeEl, {
-        pos: [10, 10],
-        deltaX: 300
-      })
+          expect().nothing()
+          restorePointerEvents()
+          resolve()
+        }
 
-      mockSwipeGesture(swipeEl, {
-        pos: [300, 10],
-        deltaX: -300
+        // eslint-disable-next-line no-new
+        new Swipe(swipeEl, {
+          endCallback: callback
+        })
+        mockSwipeGesture(swipeEl, {
+          pos: [10, 10],
+          deltaX: 300
+        })
+
+        mockSwipeGesture(swipeEl, {
+          pos: [300, 10],
+          deltaX: -300
+        })
       })
     })
   })
@@ -170,53 +176,57 @@ describe('Swipe', () => {
       expect(swipe._handleSwipe).not.toHaveBeenCalled()
     })
 
-    it('should allow swipeRight and call "rightCallback" with pointer events', done => {
-      if (!supportPointerEvent) {
-        expect().nothing()
-        done()
-        return
-      }
-
-      const style = '#fixture .pointer-event { touch-action: none !important; }'
-      fixtureEl.innerHTML += style
-
-      defineDocumentElementOntouchstart()
-      // eslint-disable-next-line no-new
-      new Swipe(swipeEl, {
-        rightCallback: () => {
-          deleteDocumentElementOntouchstart()
+    it('should allow swipeRight and call "rightCallback" with pointer events', () => {
+      return new Promise(resolve => {
+        if (!supportPointerEvent) {
           expect().nothing()
-          done()
+          resolve()
+          return
         }
-      })
 
-      mockSwipeGesture(swipeEl, { deltaX: 300 }, 'pointer')
-    })
+        const style = '#fixture .pointer-event { touch-action: none !important; }'
+        fixtureEl.innerHTML += style
 
-    it('should allow swipeLeft and call "leftCallback" with pointer events', done => {
-      if (!supportPointerEvent) {
-        expect().nothing()
-        done()
-        return
-      }
+        defineDocumentElementOntouchstart()
+        // eslint-disable-next-line no-new
+        new Swipe(swipeEl, {
+          rightCallback: () => {
+            deleteDocumentElementOntouchstart()
+            expect().nothing()
+            resolve()
+          }
+        })
 
-      const style = '#fixture .pointer-event { touch-action: none !important; }'
-      fixtureEl.innerHTML += style
+        mockSwipeGesture(swipeEl, { deltaX: 300 }, 'pointer')
+      })
+    })
 
-      defineDocumentElementOntouchstart()
-      // eslint-disable-next-line no-new
-      new Swipe(swipeEl, {
-        leftCallback: () => {
+    it('should allow swipeLeft and call "leftCallback" with pointer events', () => {
+      return new Promise(resolve => {
+        if (!supportPointerEvent) {
           expect().nothing()
-          deleteDocumentElementOntouchstart()
-          done()
+          resolve()
+          return
         }
-      })
 
-      mockSwipeGesture(swipeEl, {
-        pos: [300, 10],
-        deltaX: -300
-      }, 'pointer')
+        const style = '#fixture .pointer-event { touch-action: none !important; }'
+        fixtureEl.innerHTML += style
+
+        defineDocumentElementOntouchstart()
+        // eslint-disable-next-line no-new
+        new Swipe(swipeEl, {
+          leftCallback: () => {
+            expect().nothing()
+            deleteDocumentElementOntouchstart()
+            resolve()
+          }
+        })
+
+        mockSwipeGesture(swipeEl, {
+          pos: [300, 10],
+          deltaX: -300
+        }, 'pointer')
+      })
     })
   })