aboutsummaryrefslogtreecommitdiff
path: root/js/tests/unit/dropdown.spec.js
diff options
context:
space:
mode:
authorXhmikosR <[email protected]>2021-08-18 07:29:56 +0300
committerGitHub <[email protected]>2021-08-18 07:29:56 +0300
commit433a148c9e61aa942801fd8101dfa5c4045fdaed (patch)
treef41db59fd06019169df5ea0338213ec0e298f226 /js/tests/unit/dropdown.spec.js
parentb97cfa163b5098db70e03b27c91fca5dde9c267e (diff)
parent18b3e1ac71f73d006756684a285c5a818e2d1454 (diff)
downloadbootstrap-global-focus-vars.tar.xz
bootstrap-global-focus-vars.zip
Merge branch 'main' into global-focus-varsglobal-focus-vars
Diffstat (limited to 'js/tests/unit/dropdown.spec.js')
-rw-r--r--js/tests/unit/dropdown.spec.js436
1 files changed, 394 insertions, 42 deletions
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index 658cb65b0..2b6d8cd78 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -1,8 +1,9 @@
import Dropdown from '../../src/dropdown'
import EventHandler from '../../src/dom/event-handler'
+import { noop } from '../../src/util'
/** Test helpers */
-import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
+import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
describe('Dropdown', () => {
let fixtureEl
@@ -40,24 +41,22 @@ describe('Dropdown', () => {
})
describe('constructor', () => {
- it('should add a listener on trigger which do not have data-bs-toggle="dropdown"', () => {
+ it('should take care of element either passed as a CSS selector or DOM element', () => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn">Dropdown</button>',
+ ' <button 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="#">Link</a>',
' </div>',
'</div>'
].join('')
- const btnDropdown = fixtureEl.querySelector('.btn')
- const dropdown = new Dropdown(btnDropdown)
-
- spyOn(dropdown, 'toggle')
-
- btnDropdown.click()
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownBySelector = new Dropdown('[data-bs-toggle="dropdown"]')
+ const dropdownByElement = new Dropdown(btnDropdown)
- expect(dropdown.toggle).toHaveBeenCalled()
+ expect(dropdownBySelector._element).toEqual(btnDropdown)
+ expect(dropdownByElement._element).toEqual(btnDropdown)
})
it('should create offset modifier correctly when offset option is a function', done => {
@@ -197,18 +196,17 @@ describe('Dropdown', () => {
const firstDropdownEl = fixtureEl.querySelector('.first')
const secondDropdownEl = fixtureEl.querySelector('.second')
const dropdown1 = new Dropdown(btnDropdown1)
- const dropdown2 = new Dropdown(btnDropdown2)
firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown1.classList.contains('show')).toEqual(true)
spyOn(dropdown1._popper, 'destroy')
- dropdown2.toggle()
+ btnDropdown2.click()
})
- secondDropdownEl.addEventListener('shown.bs.dropdown', () => {
+ secondDropdownEl.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
expect(dropdown1._popper.destroy).toHaveBeenCalled()
done()
- })
+ }))
dropdown1.toggle()
})
@@ -234,7 +232,7 @@ describe('Dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- expect(EventHandler.on).toHaveBeenCalled()
+ expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
dropdown.toggle()
})
@@ -242,7 +240,7 @@ describe('Dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(false)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
- expect(EventHandler.off).toHaveBeenCalled()
+ expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
document.documentElement.ontouchstart = defaultValueOnTouchStart
done()
@@ -449,6 +447,7 @@ describe('Dropdown', () => {
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const virtualElement = {
+ nodeType: 1,
getBoundingClientRect() {
return {
width: 0,
@@ -725,7 +724,7 @@ describe('Dropdown', () => {
it('should hide a dropdown', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <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>',
@@ -738,6 +737,7 @@ describe('Dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(dropdownMenu.classList.contains('show')).toEqual(false)
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
done()
})
@@ -876,6 +876,39 @@ describe('Dropdown', () => {
done()
})
})
+
+ 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('')
+
+ const defaultValueOnTouchStart = document.documentElement.ontouchstart
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
+
+ document.documentElement.ontouchstart = () => {}
+ spyOn(EventHandler, 'off')
+
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ dropdown.hide()
+ })
+
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(btnDropdown.classList.contains('show')).toEqual(false)
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+ expect(EventHandler.off).toHaveBeenCalled()
+
+ document.documentElement.ontouchstart = defaultValueOnTouchStart
+ done()
+ })
+
+ dropdown.show()
+ })
})
describe('dispose', () => {
@@ -890,21 +923,19 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- spyOn(btnDropdown, 'addEventListener').and.callThrough()
- spyOn(btnDropdown, 'removeEventListener').and.callThrough()
const dropdown = new Dropdown(btnDropdown)
expect(dropdown._popper).toBeNull()
- expect(dropdown._menu).toBeDefined()
- expect(dropdown._element).toBeDefined()
- expect(btnDropdown.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Boolean))
+ expect(dropdown._menu).not.toBeNull()
+ expect(dropdown._element).not.toBeNull()
+ spyOn(EventHandler, 'off')
dropdown.dispose()
expect(dropdown._menu).toBeNull()
expect(dropdown._element).toBeNull()
- expect(btnDropdown.removeEventListener).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Boolean))
+ expect(EventHandler.off).toHaveBeenCalledWith(btnDropdown, Dropdown.EVENT_KEY)
})
it('should dispose dropdown with Popper', () => {
@@ -922,9 +953,9 @@ describe('Dropdown', () => {
dropdown.toggle()
- expect(dropdown._popper).toBeDefined()
- expect(dropdown._menu).toBeDefined()
- expect(dropdown._element).toBeDefined()
+ expect(dropdown._popper).not.toBeNull()
+ expect(dropdown._menu).not.toBeNull()
+ expect(dropdown._element).not.toBeNull()
dropdown.dispose()
@@ -950,7 +981,7 @@ describe('Dropdown', () => {
dropdown.toggle()
- expect(dropdown._popper).toBeDefined()
+ expect(dropdown._popper).not.toBeNull()
spyOn(dropdown._popper, 'update')
spyOn(dropdown, '_detectNavbar')
@@ -1002,13 +1033,13 @@ describe('Dropdown', () => {
showEventTriggered = true
})
- btnDropdown.addEventListener('shown.bs.dropdown', e => {
+ btnDropdown.addEventListener('shown.bs.dropdown', e => setTimeout(() => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
expect(showEventTriggered).toEqual(true)
expect(e.relatedTarget).toEqual(btnDropdown)
document.body.click()
- })
+ }))
btnDropdown.addEventListener('hide.bs.dropdown', () => {
hideEventTriggered = true
@@ -1050,6 +1081,47 @@ describe('Dropdown', () => {
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('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
+
+ const hideSpy = spyOn(dropdown, '_completeHide')
+
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ const clickEvent = new MouseEvent('click', {
+ bubbles: true
+ })
+
+ dropdownMenu.querySelector('option').dispatchEvent(clickEvent)
+ })
+
+ dropdownMenu.addEventListener('click', event => {
+ expect(event.target.tagName).toMatch(/select|option/i)
+
+ Dropdown.clearMenus(event)
+
+ setTimeout(() => {
+ expect(hideSpy).not.toHaveBeenCalled()
+ done()
+ }, 10)
+ })
+
+ dropdown.show()
+ })
+
it('should manage bs attribute `data-bs-popper`="none" when dropdown is in navbar', done => {
fixtureEl.innerHTML = [
'<nav class="navbar navbar-expand-md navbar-light bg-light">',
@@ -1094,7 +1166,7 @@ describe('Dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
// Popper adds this attribute when we use it
- expect(dropdownMenu.getAttribute('x-placement')).toEqual(null)
+ expect(dropdownMenu.getAttribute('data-popper-placement')).toEqual(null)
done()
})
@@ -1467,7 +1539,7 @@ describe('Dropdown', () => {
triggerDropdown.click()
})
- it('should focus on the first element when using ArrowUp for the first time', done => {
+ 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>',
@@ -1479,22 +1551,47 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const item1 = fixtureEl.querySelector('#item1')
+ const lastItem = fixtureEl.querySelector('#item2')
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowUp'
+ setTimeout(() => {
+ expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
+ done()
+ })
+ })
- document.activeElement.dispatchEvent(keydown)
- expect(document.activeElement).toEqual(item1, 'item1 is focused')
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowUp'
+ triggerDropdown.dispatchEvent(keydown)
+ })
- done()
+ 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('')
+
+ 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.click()
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
+ triggerDropdown.dispatchEvent(keydown)
})
- it('should not close the dropdown if the user clicks on a text field', done => {
+ 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>',
@@ -1520,7 +1617,7 @@ describe('Dropdown', () => {
triggerDropdown.click()
})
- it('should not close the dropdown if the user clicks on a textarea', done => {
+ 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>',
@@ -1546,6 +1643,33 @@ describe('Dropdown', () => {
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">'
+ ]
+
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const input = fixtureEl.querySelector('input')
+
+ triggerDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect().nothing()
+ done()
+ })
+
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ input.dispatchEvent(createEvent('click', {
+ bubbles: true
+ }))
+ })
+
+ triggerDropdown.click()
+ })
+
it('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
@@ -1653,6 +1777,133 @@ describe('Dropdown', () => {
done()
}, 20)
})
+
+ 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>'
+ ]
+
+ 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()
+ done()
+ })
+
+ 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 (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>'
+ ]
+
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+
+ const expectDropdownToBeOpened = () => setTimeout(() => {
+ expect(dropdownToggle.classList.contains('show')).toEqual(true)
+ dropdownMenu.click()
+ }, 150)
+
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ document.documentElement.click()
+ expectDropdownToBeOpened()
+ })
+
+ dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
+ expect(dropdownToggle.classList.contains('show')).toEqual(false)
+ done()
+ }))
+
+ 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>'
+ ]
+
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+
+ const expectDropdownToBeOpened = () => setTimeout(() => {
+ expect(dropdownToggle.classList.contains('show')).toEqual(true)
+ document.documentElement.click()
+ }, 150)
+
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ dropdownMenu.click()
+ expectDropdownToBeOpened()
+ })
+
+ dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdownToggle.classList.contains('show')).toEqual(false)
+ done()
+ })
+
+ 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>'
+ ]
+
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+
+ const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
+ expect(dropdownToggle.classList.contains('show')).toEqual(true)
+ if (shouldTriggerClick) {
+ document.documentElement.click()
+ } else {
+ done()
+ }
+
+ expectDropdownToBeOpened(false)
+ }, 150)
+
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ dropdownMenu.click()
+ expectDropdownToBeOpened()
+ })
+
+ dropdownToggle.click()
+ })
})
describe('jQueryInterface', () => {
@@ -1666,7 +1917,7 @@ describe('Dropdown', () => {
jQueryMock.fn.dropdown.call(jQueryMock)
- expect(Dropdown.getInstance(div)).toBeDefined()
+ expect(Dropdown.getInstance(div)).not.toBeNull()
})
it('should not re create a dropdown', () => {
@@ -1718,6 +1969,60 @@ describe('Dropdown', () => {
})
})
+ describe('getOrCreateInstance', () => {
+ it('should return dropdown instance', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const dropdown = new Dropdown(div)
+
+ expect(Dropdown.getOrCreateInstance(div)).toEqual(dropdown)
+ expect(Dropdown.getInstance(div)).toEqual(Dropdown.getOrCreateInstance(div, {}))
+ expect(Dropdown.getOrCreateInstance(div)).toBeInstanceOf(Dropdown)
+ })
+
+ it('should return new instance when there is no dropdown instance', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+
+ expect(Dropdown.getInstance(div)).toEqual(null)
+ expect(Dropdown.getOrCreateInstance(div)).toBeInstanceOf(Dropdown)
+ })
+
+ it('should return new instance when there is no dropdown instance with given configuration', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+
+ expect(Dropdown.getInstance(div)).toEqual(null)
+ const dropdown = Dropdown.getOrCreateInstance(div, {
+ display: 'dynamic'
+ })
+ expect(dropdown).toBeInstanceOf(Dropdown)
+
+ expect(dropdown._config.display).toEqual('dynamic')
+ })
+
+ it('should return the instance when exists without given configuration', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const dropdown = new Dropdown(div, {
+ display: 'dynamic'
+ })
+ expect(Dropdown.getInstance(div)).toEqual(dropdown)
+
+ const dropdown2 = Dropdown.getOrCreateInstance(div, {
+ display: 'static'
+ })
+ expect(dropdown).toBeInstanceOf(Dropdown)
+ expect(dropdown2).toEqual(dropdown)
+
+ expect(dropdown2._config.display).toEqual('dynamic')
+ })
+ })
+
it('should open dropdown when pressing keydown or keyup', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
@@ -1765,4 +2070,51 @@ describe('Dropdown', () => {
triggerDropdown.dispatchEvent(keydown)
})
+
+ it('should allow `data-bs-toggle="dropdown"` click events to bubble up', () => {
+ 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 clickListener = jasmine.createSpy('clickListener')
+ const delegatedClickListener = jasmine.createSpy('delegatedClickListener')
+
+ btnDropdown.addEventListener('click', clickListener)
+ document.addEventListener('click', delegatedClickListener)
+
+ btnDropdown.click()
+
+ expect(clickListener).toHaveBeenCalled()
+ 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('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const childElement = fixtureEl.querySelector('#childElement')
+
+ btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ done()
+ }))
+
+ childElement.click()
+ })
})