',
- '
',
- '
',
- ' Working Tab 1 (#tab1) Content Here',
- '
',
- '
',
- ' Working Tab 2 (#2) with numeric ID',
- '
',
- '
'
- ].join('')
- const profileTriggerEl = fixtureEl.querySelector('#trigger2')
- const tab = new Tab(profileTriggerEl)
-
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector(`#${CSS.escape('2')}`)).toHaveClass('active')
- done()
- })
-
- tab.show()
- })
-
- it('should not fire shown when show is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = '
'
-
- const navEl = fixtureEl.querySelector('.nav > div')
- const tab = new Tab(navEl)
- const expectDone = () => {
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 30)
- }
-
- navEl.addEventListener('show.bs.tab', ev => {
- ev.preventDefault()
- expectDone()
- })
-
- navEl.addEventListener('shown.bs.tab', () => {
- reject(new Error('should not trigger shown event'))
- })
-
- tab.show()
- })
- })
-
- it('should not fire shown when tab is already active', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- '
'
- ].join('')
-
- const triggerActive = fixtureEl.querySelector('button.active')
- const tab = new Tab(triggerActive)
-
- triggerActive.addEventListener('shown.bs.tab', () => {
- reject(new Error('should not trigger shown event'))
- })
-
- tab.show()
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 30)
- })
- })
-
- it('show and shown events should reference correct relatedTarget', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- '
'
- ].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('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()
- })
-
- secondTab.show()
- })
- })
-
- it('should fire hide and hidden events', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
'
- ].join('')
-
- 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()
- })
-
- 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')
- resolve()
- })
-
- firstTab.show()
- })
- })
-
- it('should not fire hidden when hide is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
'
- ].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('hide.bs.tab', ev => {
- ev.preventDefault()
- expectDone()
- })
-
- triggerList[0].addEventListener('hidden.bs.tab', () => {
- reject(new Error('should not trigger hidden'))
- })
-
- firstTab.show()
- })
- })
-
- it('should handle removed tabs', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' - ',
- ' ',
- ' ',
- ' ',
- '
',
- ' - ',
- ' ',
- ' ',
- ' ',
- '
',
- ' - ',
- ' ',
- ' ',
- ' ',
- '
',
- '
',
- '
',
- '
test 1
',
- '
test 2
',
- '
test 3
',
- '
'
- ].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()
- })
-
- 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()
- })
-
- btnCloseEl.click()
- })
- })
-
- it('should not focus on opened tab', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- '
'
- ].join('')
-
- const firstTab = fixtureEl.querySelector('#home')
- firstTab.focus()
-
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
- const tab = new Tab(profileTriggerEl)
-
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(document.activeElement).toBe(firstTab)
- expect(document.activeElement).not.toBe(profileTriggerEl)
- resolve()
- })
-
- tab.show()
- })
- })
- })
-
- describe('dispose', () => {
- it('should dispose a tab', () => {
- fixtureEl.innerHTML = '
'
-
- const el = fixtureEl.querySelector('.nav > div')
- const tab = new Tab(fixtureEl.querySelector('.nav > div'))
-
- expect(Tab.getInstance(el)).not.toBeNull()
-
- tab.dispose()
-
- expect(Tab.getInstance(el)).toBeNull()
- })
- })
-
- describe('_activate', () => {
- it('should not be called if element argument is null', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const tabEl = fixtureEl.querySelector('.nav-link')
- const tab = new Tab(tabEl)
- const spy = jasmine.createSpy('spy')
-
- const spyQueue = spyOn(tab, '_queueCallback')
- tab._activate(null, spy)
- expect(spyQueue).not.toHaveBeenCalled()
- expect(spy).not.toHaveBeenCalled()
- })
- })
-
- describe('_setInitialAttributes', () => {
- it('should put aria attributes', () => {
- fixtureEl.innerHTML = [
- '
',
- '
',
- '
'
- ].join('')
-
- const tabEl = fixtureEl.querySelector('.nav-link')
- const parent = fixtureEl.querySelector('.nav')
- const children = fixtureEl.querySelectorAll('.nav-link')
- const tabPanel = fixtureEl.querySelector('#panel')
- const tabPanel2 = fixtureEl.querySelector('#panel2')
-
- expect(parent.getAttribute('role')).toEqual(null)
- expect(tabEl.getAttribute('role')).toEqual(null)
- expect(tabPanel.getAttribute('role')).toEqual(null)
- const tab = new Tab(tabEl)
- tab._setInitialAttributes(parent, children)
-
- expect(parent.getAttribute('role')).toEqual('tablist')
- expect(tabEl.getAttribute('role')).toEqual('tab')
-
- expect(tabPanel.getAttribute('role')).toEqual('tabpanel')
- expect(tabPanel2.getAttribute('role')).toEqual('tabpanel')
- expect(tabPanel.hasAttribute('tabindex')).toBeFalse()
- expect(tabPanel.hasAttribute('tabindex2')).toBeFalse()
-
- expect(tabPanel.getAttribute('aria-labelledby')).toEqual('foo')
- expect(tabPanel2.hasAttribute('aria-labelledby')).toBeFalse()
- })
- })
-
- describe('_keydown', () => {
- it('if event is not one of left/right/up/down arrow, ignore it', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const tabEl = fixtureEl.querySelector('.nav-link')
- const tab = new Tab(tabEl)
-
- const keydown = createEvent('keydown')
- keydown.key = 'Enter'
- const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
- const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
- const spyKeydown = spyOn(tab, '_keydown')
- const spyGet = spyOn(tab, '_getChildren')
-
- tabEl.dispatchEvent(keydown)
- expect(spyKeydown).toHaveBeenCalled()
- expect(spyGet).not.toHaveBeenCalled()
-
- expect(spyStop).not.toHaveBeenCalled()
- expect(spyPrevent).not.toHaveBeenCalled()
- })
-
- it('if keydown event is right/down arrow, handle it', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tabEl3 = fixtureEl.querySelector('#tab3')
- const tab1 = new Tab(tabEl1)
- const tab2 = new Tab(tabEl2)
- const tab3 = new Tab(tabEl3)
- const spyShow1 = spyOn(tab1, 'show').and.callThrough()
- const spyShow2 = spyOn(tab2, 'show').and.callThrough()
- const spyShow3 = spyOn(tab3, 'show').and.callThrough()
- const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
- const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
- const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
-
- const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
- const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
- let keydown = createEvent('keydown')
- keydown.key = 'ArrowRight'
-
- tabEl1.dispatchEvent(keydown)
- expect(spyShow2).toHaveBeenCalled()
- expect(spyFocus2).toHaveBeenCalled()
-
- keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
-
- tabEl2.dispatchEvent(keydown)
- expect(spyShow3).toHaveBeenCalled()
- expect(spyFocus3).toHaveBeenCalled()
-
- tabEl3.dispatchEvent(keydown)
- expect(spyShow1).toHaveBeenCalled()
- expect(spyFocus1).toHaveBeenCalled()
-
- expect(spyStop).toHaveBeenCalledTimes(3)
- expect(spyPrevent).toHaveBeenCalledTimes(3)
- })
-
- it('if keydown event is left arrow, handle it', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tab1 = new Tab(tabEl1)
- const tab2 = new Tab(tabEl2)
- const spyShow1 = spyOn(tab1, 'show').and.callThrough()
- const spyShow2 = spyOn(tab2, 'show').and.callThrough()
- const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
- const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
-
- const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
- const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
- let keydown = createEvent('keydown')
- keydown.key = 'ArrowLeft'
-
- tabEl2.dispatchEvent(keydown)
- expect(spyShow1).toHaveBeenCalled()
- expect(spyFocus1).toHaveBeenCalled()
-
- keydown = createEvent('keydown')
- keydown.key = 'ArrowUp'
-
- tabEl1.dispatchEvent(keydown)
- expect(spyShow2).toHaveBeenCalled()
- expect(spyFocus2).toHaveBeenCalled()
-
- expect(spyStop).toHaveBeenCalledTimes(2)
- expect(spyPrevent).toHaveBeenCalledTimes(2)
- })
-
- it('if keydown event is Home, handle it', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl3 = fixtureEl.querySelector('#tab3')
-
- const tab3 = new Tab(tabEl3)
- tab3.show()
-
- const spyShown = jasmine.createSpy()
- tabEl1.addEventListener('shown.bs.tab', spyShown)
-
- const keydown = createEvent('keydown')
- keydown.key = 'Home'
-
- tabEl3.dispatchEvent(keydown)
-
- expect(spyShown).toHaveBeenCalled()
- })
-
- it('if keydown event is End, handle it', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl3 = fixtureEl.querySelector('#tab3')
-
- const tab1 = new Tab(tabEl1)
- tab1.show()
-
- const spyShown = jasmine.createSpy()
- tabEl3.addEventListener('shown.bs.tab', spyShown)
-
- const keydown = createEvent('keydown')
- keydown.key = 'End'
-
- tabEl1.dispatchEvent(keydown)
-
- expect(spyShown).toHaveBeenCalled()
- })
-
- it('if keydown event is right arrow and next element is disabled', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tabEl3 = fixtureEl.querySelector('#tab3')
- const tabEl4 = fixtureEl.querySelector('#tab4')
- const tab1 = new Tab(tabEl1)
- const tab2 = new Tab(tabEl2)
- const tab3 = new Tab(tabEl3)
- const tab4 = new Tab(tabEl4)
- const spy1 = spyOn(tab1, 'show').and.callThrough()
- const spy2 = spyOn(tab2, 'show').and.callThrough()
- const spy3 = spyOn(tab3, 'show').and.callThrough()
- const spy4 = spyOn(tab4, 'show').and.callThrough()
- const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
- const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
- const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
- const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
-
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowRight'
-
- tabEl1.dispatchEvent(keydown)
- expect(spy1).not.toHaveBeenCalled()
- expect(spy2).not.toHaveBeenCalled()
- expect(spy3).not.toHaveBeenCalled()
- expect(spy4).toHaveBeenCalledTimes(1)
- expect(spyFocus1).not.toHaveBeenCalled()
- expect(spyFocus2).not.toHaveBeenCalled()
- expect(spyFocus3).not.toHaveBeenCalled()
- expect(spyFocus4).toHaveBeenCalledTimes(1)
- })
-
- it('if keydown event is left arrow and next element is disabled', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tabEl3 = fixtureEl.querySelector('#tab3')
- const tabEl4 = fixtureEl.querySelector('#tab4')
- const tab1 = new Tab(tabEl1)
- const tab2 = new Tab(tabEl2)
- const tab3 = new Tab(tabEl3)
- const tab4 = new Tab(tabEl4)
- const spy1 = spyOn(tab1, 'show').and.callThrough()
- const spy2 = spyOn(tab2, 'show').and.callThrough()
- const spy3 = spyOn(tab3, 'show').and.callThrough()
- const spy4 = spyOn(tab4, 'show').and.callThrough()
- const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
- const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
- const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
- const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
-
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowLeft'
-
- tabEl4.dispatchEvent(keydown)
- expect(spy4).not.toHaveBeenCalled()
- expect(spy3).not.toHaveBeenCalled()
- expect(spy2).not.toHaveBeenCalled()
- expect(spy1).toHaveBeenCalledTimes(1)
- expect(spyFocus4).not.toHaveBeenCalled()
- expect(spyFocus3).not.toHaveBeenCalled()
- expect(spyFocus2).not.toHaveBeenCalled()
- expect(spyFocus1).toHaveBeenCalledTimes(1)
- })
-
- it('if keydown event is Home and first element is disabled', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tabEl3 = fixtureEl.querySelector('#tab3')
- const tab3 = new Tab(tabEl3)
-
- tab3.show()
-
- const spyShown1 = jasmine.createSpy()
- const spyShown2 = jasmine.createSpy()
- tabEl1.addEventListener('shown.bs.tab', spyShown1)
- tabEl2.addEventListener('shown.bs.tab', spyShown2)
-
- const keydown = createEvent('keydown')
- keydown.key = 'Home'
-
- tabEl3.dispatchEvent(keydown)
-
- expect(spyShown1).not.toHaveBeenCalled()
- expect(spyShown2).toHaveBeenCalled()
- })
-
- it('if keydown event is End and last element is disabled', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const tabEl1 = fixtureEl.querySelector('#tab1')
- const tabEl2 = fixtureEl.querySelector('#tab2')
- const tabEl3 = fixtureEl.querySelector('#tab3')
- const tab1 = new Tab(tabEl1)
-
- tab1.show()
-
- const spyShown2 = jasmine.createSpy()
- const spyShown3 = jasmine.createSpy()
- tabEl2.addEventListener('shown.bs.tab', spyShown2)
- tabEl3.addEventListener('shown.bs.tab', spyShown3)
-
- const keydown = createEvent('keydown')
- keydown.key = 'End'
-
- tabEl1.dispatchEvent(keydown)
-
- expect(spyShown3).not.toHaveBeenCalled()
- expect(spyShown2).toHaveBeenCalled()
- })
- })
-
- describe('jQueryInterface', () => {
- it('should create a tab', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('.nav > div')
-
- jQueryMock.fn.tab = Tab.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tab.call(jQueryMock)
-
- expect(Tab.getInstance(div)).not.toBeNull()
- })
-
- it('should not re create a tab', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('.nav > div')
- const tab = new Tab(div)
-
- jQueryMock.fn.tab = Tab.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tab.call(jQueryMock)
-
- expect(Tab.getInstance(div)).toEqual(tab)
- })
-
- it('should call a tab method', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('.nav > div')
- const tab = new Tab(div)
-
- const spy = spyOn(tab, 'show')
-
- jQueryMock.fn.tab = Tab.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tab.call(jQueryMock, 'show')
-
- expect(Tab.getInstance(div)).toEqual(tab)
- expect(spy).toHaveBeenCalled()
- })
-
- it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('.nav > div')
- const action = 'undefinedMethod'
-
- jQueryMock.fn.tab = Tab.jQueryInterface
- jQueryMock.elements = [div]
-
- expect(() => {
- jQueryMock.fn.tab.call(jQueryMock, action)
- }).toThrowError(TypeError, `No method named "${action}"`)
- })
- })
-
- describe('getInstance', () => {
- it('should return null if there is no instance', () => {
- expect(Tab.getInstance(fixtureEl)).toBeNull()
- })
-
- it('should return this instance', () => {
- fixtureEl.innerHTML = '
'
-
- const divEl = fixtureEl.querySelector('.nav > div')
- const tab = new Tab(divEl)
-
- expect(Tab.getInstance(divEl)).toEqual(tab)
- expect(Tab.getInstance(divEl)).toBeInstanceOf(Tab)
- })
- })
-
- describe('getOrCreateInstance', () => {
- it('should return tab instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tab = new Tab(div)
-
- expect(Tab.getOrCreateInstance(div)).toEqual(tab)
- expect(Tab.getInstance(div)).toEqual(Tab.getOrCreateInstance(div, {}))
- expect(Tab.getOrCreateInstance(div)).toBeInstanceOf(Tab)
- })
-
- it('should return new instance when there is no tab instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Tab.getInstance(div)).toBeNull()
- expect(Tab.getOrCreateInstance(div)).toBeInstanceOf(Tab)
- })
- })
-
- describe('data-api', () => {
- it('should create dynamically a tab', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- '
'
- ].join('')
-
- const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
-
- secondTabTrigger.addEventListener('shown.bs.tab', () => {
- expect(secondTabTrigger).toHaveClass('active')
- expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
- resolve()
- })
-
- secondTabTrigger.click()
- })
- })
-
- it('selected tab should deactivate previous selected link in dropdown', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const firstLiLinkEl = fixtureEl.querySelector('li:first-child a')
-
- firstLiLinkEl.click()
- expect(firstLiLinkEl).toHaveClass('active')
- expect(fixtureEl.querySelector('li:last-child a')).not.toHaveClass('active')
- expect(fixtureEl.querySelector('li:last-child .dropdown-menu a:first-child')).not.toHaveClass('active')
- })
-
- it('selecting a dropdown tab does not activate another', () => {
- const nav1 = [
- '
'
- ].join('')
- const nav2 = [
- '
'
- ].join('')
-
- fixtureEl.innerHTML = nav1 + nav2
-
- const firstDropItem = fixtureEl.querySelector('#nav1 .dropdown-item')
-
- firstDropItem.click()
- expect(firstDropItem).toHaveClass('active')
- expect(fixtureEl.querySelector('#nav1 .dropdown-toggle')).toHaveClass('active')
- expect(fixtureEl.querySelector('#nav2 .dropdown-toggle')).not.toHaveClass('active')
- expect(fixtureEl.querySelector('#nav2 .dropdown-item')).not.toHaveClass('active')
- })
-
- it('should support li > .dropdown-item', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const dropItems = fixtureEl.querySelectorAll('.dropdown-item')
-
- dropItems[1].click()
- expect(dropItems[0]).not.toHaveClass('active')
- expect(dropItems[1]).toHaveClass('active')
- expect(fixtureEl.querySelector('.nav-link')).not.toHaveClass('active')
- })
-
- it('should handle nested tabs', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- '
',
- '
',
- '
',
- '
',
- '
Nested Tab1 Content
',
- '
Nested Tab2 Content
',
- '
',
- '
',
- '
Tab2 Content
',
- '
Tab3 Content
',
- '
'
- ].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()
- })
-
- tab1El.addEventListener('shown.bs.tab', () => {
- expect(xTab1El).toHaveClass('active')
- tabNested2El.click()
- })
-
- tab1El.click()
- })
- })
-
- it('should not remove fade class if no active pane is present', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- '
'
- ].join('')
-
- const triggerTabProfileEl = fixtureEl.querySelector('#tab-profile')
- const triggerTabHomeEl = fixtureEl.querySelector('#tab-home')
- const tabProfileEl = fixtureEl.querySelector('#profile')
- const tabHomeEl = fixtureEl.querySelector('#home')
-
- triggerTabHomeEl.addEventListener('shown.bs.tab', () => {
- setTimeout(() => {
- expect(tabProfileEl).toHaveClass('fade')
- expect(tabProfileEl).not.toHaveClass('show')
-
- expect(tabHomeEl).toHaveClass('fade')
- expect(tabHomeEl).toHaveClass('show')
-
- resolve()
- }, 10)
- })
-
- triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
- setTimeout(() => {
- expect(tabProfileEl).toHaveClass('fade')
- expect(tabProfileEl).toHaveClass('show')
- triggerTabHomeEl.click()
- }, 10)
- })
-
- triggerTabProfileEl.click()
- })
- })
-
- it('should add `show` class to tab panes if there is no `.fade` class', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' - ',
- ' ',
- '
',
- ' - ',
- ' ',
- '
',
- '
',
- '
',
- '
test 1
',
- '
test 2
',
- '
'
- ].join('')
-
- const secondNavEl = fixtureEl.querySelector('#secondNav')
-
- secondNavEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelectorAll('.tab-content .show')).toHaveSize(1)
- resolve()
- })
-
- secondNavEl.click()
- })
- })
-
- it('should add show class to tab panes if there is a `.fade` class', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' - ',
- ' ',
- '
',
- ' - ',
- ' ',
- '
',
- '
',
- '
',
- '
test 1
',
- '
test 2
',
- '
'
- ].join('')
-
- const secondNavEl = fixtureEl.querySelector('#secondNav')
-
- secondNavEl.addEventListener('shown.bs.tab', () => {
- setTimeout(() => {
- expect(fixtureEl.querySelectorAll('.show')).toHaveSize(1)
- resolve()
- }, 10)
- })
-
- secondNavEl.click()
- })
- })
-
- it('should prevent default when the trigger is
or ', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- ''
- ].join('')
-
- const tabEl = fixtureEl.querySelector('[href="#test2"]')
- const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
- tabEl.addEventListener('shown.bs.tab', () => {
- expect(tabEl).toHaveClass('active')
- expect(spy).toHaveBeenCalled()
- resolve()
- })
-
- tabEl.click()
- })
- })
-
- it('should not fire shown when tab has disabled attribute', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '',
- ' ',
- ' ',
- '
',
- ''
- ].join('')
-
- const triggerDisabled = fixtureEl.querySelector('button[disabled]')
- triggerDisabled.addEventListener('shown.bs.tab', () => {
- reject(new Error('should not trigger shown event'))
- })
-
- triggerDisabled.click()
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 30)
- })
- })
-
- it('should not fire shown when tab has disabled class', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '',
- ''
- ].join('')
-
- const triggerDisabled = fixtureEl.querySelector('a.disabled')
-
- triggerDisabled.addEventListener('shown.bs.tab', () => {
- reject(new Error('should not trigger shown event'))
- })
-
- triggerDisabled.click()
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 30)
- })
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/toast.spec.js b/bootstrap/js/tests/unit/toast.spec.js
deleted file mode 100644
index 200fe3e..0000000
--- a/bootstrap/js/tests/unit/toast.spec.js
+++ /dev/null
@@ -1,672 +0,0 @@
-import Toast from '../../src/toast.js'
-import {
- clearFixture, createEvent, getFixture, jQueryMock
-} from '../helpers/fixture.js'
-
-describe('Toast', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- })
-
- describe('VERSION', () => {
- it('should return plugin version', () => {
- expect(Toast.VERSION).toEqual(jasmine.any(String))
- })
- })
-
- describe('DATA_KEY', () => {
- it('should return plugin data key', () => {
- expect(Toast.DATA_KEY).toEqual('bs.toast')
- })
- })
-
- describe('constructor', () => {
- it('should take care of element either passed as a CSS selector or DOM element', () => {
- fixtureEl.innerHTML = ''
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toastBySelector = new Toast('.toast')
- const toastByElement = new Toast(toastEl)
-
- expect(toastBySelector._element).toEqual(toastEl)
- expect(toastByElement._element).toEqual(toastEl)
- })
-
- it('should allow to config in js', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl, {
- delay: 1
- })
-
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl).toHaveClass('show')
- resolve()
- })
-
- toast.show()
- })
- })
-
- it('should close toast when close element with data-bs-dismiss attribute is set', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- ' ',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
-
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl).toHaveClass('show')
-
- const button = toastEl.querySelector('.btn-close')
-
- button.click()
- })
-
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl).not.toHaveClass('show')
- resolve()
- })
-
- toast.show()
- })
- })
- })
-
- describe('Default', () => {
- it('should expose default setting to allow to override them', () => {
- const defaultDelay = 1000
-
- Toast.Default.delay = defaultDelay
-
- fixtureEl.innerHTML = [
- '',
- ' ',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
-
- expect(toast._config.delay).toEqual(defaultDelay)
- })
- })
-
- describe('DefaultType', () => {
- it('should expose default setting types for read', () => {
- expect(Toast.DefaultType).toEqual(jasmine.any(Object))
- })
- })
-
- describe('show', () => {
- it('should auto hide', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl).not.toHaveClass('show')
- resolve()
- })
-
- toast.show()
- })
- })
-
- it('should not add fade class', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl).not.toHaveClass('fade')
- resolve()
- })
-
- toast.show()
- })
- })
-
- it('should not trigger shown if show is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].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()
- })
-
- toastEl.addEventListener('shown.bs.toast', () => {
- reject(new Error('shown event should not be triggered if show is prevented'))
- })
-
- toast.show()
- })
- })
-
- it('should clear timeout if toast is shown again before it is hidden', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- setTimeout(() => {
- toast._config.autohide = false
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(spy).toHaveBeenCalled()
- expect(toast._timeout).toBeNull()
- resolve()
- })
- toast.show()
- }, toast._config.delay / 2)
-
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
-
- toast.show()
- })
- })
-
- it('should clear timeout if toast is interacted with mouse', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
-
- setTimeout(() => {
- spy.calls.reset()
-
- toastEl.addEventListener('mouseover', () => {
- expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
- expect(toast._timeout).toBeNull()
- resolve()
- })
-
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
-
- toast.show()
- })
- })
-
- it('should clear timeout if toast is interacted with keyboard', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '',
- '
',
- ' a simple toast',
- ' ',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
-
- setTimeout(() => {
- spy.calls.reset()
-
- toastEl.addEventListener('focusin', () => {
- expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
- expect(toast._timeout).toBeNull()
- resolve()
- })
-
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- }, toast._config.delay / 2)
-
- toast.show()
- })
- })
-
- it('should still auto hide after being interacted with mouse and keyboard', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '',
- '
',
- ' a simple toast',
- ' ',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- 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()
- })
- })
-
- it('should not auto hide if focus leaves but mouse pointer remains inside', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '',
- '
',
- ' a simple toast',
- ' ',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- setTimeout(() => {
- toastEl.addEventListener('mouseover', () => {
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- })
-
- toastEl.addEventListener('focusin', () => {
- const outsideFocusable = document.getElementById('outside-focusable')
- outsideFocusable.focus()
- })
-
- toastEl.addEventListener('focusout', () => {
- expect(toast._timeout).toBeNull()
- resolve()
- })
-
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
-
- toast.show()
- })
- })
-
- it('should not auto hide if mouse pointer leaves but focus remains inside', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '',
- '
',
- ' a simple toast',
- ' ',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- setTimeout(() => {
- toastEl.addEventListener('mouseover', () => {
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- })
-
- toastEl.addEventListener('focusin', () => {
- const mouseOutEvent = createEvent('mouseout')
- toastEl.dispatchEvent(mouseOutEvent)
- })
-
- toastEl.addEventListener('mouseout', () => {
- expect(toast._timeout).toBeNull()
- resolve()
- })
-
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
-
- toast.show()
- })
- })
- })
-
- describe('hide', () => {
- it('should allow to hide toast manually', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
-
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl).not.toHaveClass('show')
- resolve()
- })
-
- toast.show()
- })
- })
-
- it('should do nothing when we call hide on a non shown toast', () => {
- fixtureEl.innerHTML = ''
-
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
-
- const spy = spyOn(toastEl.classList, 'contains')
-
- toast.hide()
-
- expect(spy).toHaveBeenCalled()
- })
-
- it('should not trigger hidden if hide is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- const assertDone = () => {
- setTimeout(() => {
- expect(toastEl).toHaveClass('show')
- resolve()
- }, 20)
- }
-
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
-
- toastEl.addEventListener('hide.bs.toast', event => {
- event.preventDefault()
- assertDone()
- })
-
- toastEl.addEventListener('hidden.bs.toast', () => {
- reject(new Error('hidden event should not be triggered if hide is prevented'))
- })
-
- toast.show()
- })
- })
- })
-
- describe('dispose', () => {
- it('should allow to destroy toast', () => {
- fixtureEl.innerHTML = ''
-
- const toastEl = fixtureEl.querySelector('div')
-
- const toast = new Toast(toastEl)
-
- expect(Toast.getInstance(toastEl)).not.toBeNull()
-
- toast.dispose()
-
- expect(Toast.getInstance(toastEl)).toBeNull()
- })
-
- it('should allow to destroy toast and hide it before that', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- '
',
- ' a simple toast',
- '
',
- '
'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- const expected = () => {
- expect(toastEl).toHaveClass('show')
- expect(Toast.getInstance(toastEl)).not.toBeNull()
-
- toast.dispose()
-
- expect(Toast.getInstance(toastEl)).toBeNull()
- expect(toastEl).not.toHaveClass('show')
-
- resolve()
- }
-
- toastEl.addEventListener('shown.bs.toast', () => {
- setTimeout(expected, 1)
- })
-
- toast.show()
- })
- })
- })
-
- describe('jQueryInterface', () => {
- it('should create a toast', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
-
- jQueryMock.fn.toast = Toast.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.toast.call(jQueryMock)
-
- expect(Toast.getInstance(div)).not.toBeNull()
- })
-
- it('should not re create a toast', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div)
-
- jQueryMock.fn.toast = Toast.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.toast.call(jQueryMock)
-
- expect(Toast.getInstance(div)).toEqual(toast)
- })
-
- it('should call a toast method', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div)
-
- const spy = spyOn(toast, 'show')
-
- jQueryMock.fn.toast = Toast.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.toast.call(jQueryMock, 'show')
-
- expect(Toast.getInstance(div)).toEqual(toast)
- expect(spy).toHaveBeenCalled()
- })
-
- it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const action = 'undefinedMethod'
-
- jQueryMock.fn.toast = Toast.jQueryInterface
- jQueryMock.elements = [div]
-
- expect(() => {
- jQueryMock.fn.toast.call(jQueryMock, action)
- }).toThrowError(TypeError, `No method named "${action}"`)
- })
- })
-
- describe('getInstance', () => {
- it('should return a toast instance', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div)
-
- expect(Toast.getInstance(div)).toEqual(toast)
- expect(Toast.getInstance(div)).toBeInstanceOf(Toast)
- })
-
- it('should return null when there is no toast instance', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
-
- expect(Toast.getInstance(div)).toBeNull()
- })
- })
-
- describe('getOrCreateInstance', () => {
- it('should return toast instance', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div)
-
- expect(Toast.getOrCreateInstance(div)).toEqual(toast)
- expect(Toast.getInstance(div)).toEqual(Toast.getOrCreateInstance(div, {}))
- expect(Toast.getOrCreateInstance(div)).toBeInstanceOf(Toast)
- })
-
- it('should return new instance when there is no toast instance', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
-
- expect(Toast.getInstance(div)).toBeNull()
- expect(Toast.getOrCreateInstance(div)).toBeInstanceOf(Toast)
- })
-
- it('should return new instance when there is no toast instance with given configuration', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
-
- expect(Toast.getInstance(div)).toBeNull()
- const toast = Toast.getOrCreateInstance(div, {
- delay: 1
- })
- expect(toast).toBeInstanceOf(Toast)
-
- expect(toast._config.delay).toEqual(1)
- })
-
- it('should return the instance when exists without given configuration', () => {
- fixtureEl.innerHTML = ''
-
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div, {
- delay: 1
- })
- expect(Toast.getInstance(div)).toEqual(toast)
-
- const toast2 = Toast.getOrCreateInstance(div, {
- delay: 2
- })
- expect(toast).toBeInstanceOf(Toast)
- expect(toast2).toEqual(toast)
-
- expect(toast2._config.delay).toEqual(1)
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/tooltip.spec.js b/bootstrap/js/tests/unit/tooltip.spec.js
deleted file mode 100644
index ceb8de4..0000000
--- a/bootstrap/js/tests/unit/tooltip.spec.js
+++ /dev/null
@@ -1,1553 +0,0 @@
-import EventHandler from '../../src/dom/event-handler.js'
-import Tooltip from '../../src/tooltip.js'
-import { noop } from '../../src/util/index.js'
-import {
- clearFixture, createEvent, getFixture, jQueryMock
-} from '../helpers/fixture.js'
-
-describe('Tooltip', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
-
- for (const tooltipEl of document.querySelectorAll('.tooltip')) {
- tooltipEl.remove()
- }
- })
-
- describe('VERSION', () => {
- it('should return plugin version', () => {
- expect(Tooltip.VERSION).toEqual(jasmine.any(String))
- })
- })
-
- describe('Default', () => {
- it('should return plugin default config', () => {
- expect(Tooltip.Default).toEqual(jasmine.any(Object))
- })
- })
-
- describe('NAME', () => {
- it('should return plugin name', () => {
- expect(Tooltip.NAME).toEqual(jasmine.any(String))
- })
- })
-
- describe('DATA_KEY', () => {
- it('should return plugin data key', () => {
- expect(Tooltip.DATA_KEY).toEqual('bs.tooltip')
- })
- })
-
- describe('EVENT_KEY', () => {
- it('should return plugin event key', () => {
- expect(Tooltip.EVENT_KEY).toEqual('.bs.tooltip')
- })
- })
-
- describe('DefaultType', () => {
- it('should return plugin default type', () => {
- expect(Tooltip.DefaultType).toEqual(jasmine.any(Object))
- })
- })
-
- describe('constructor', () => {
- it('should take care of element either passed as a CSS selector or DOM element', () => {
- fixtureEl.innerHTML = ''
-
- const tooltipEl = fixtureEl.querySelector('#tooltipEl')
- const tooltipBySelector = new Tooltip('#tooltipEl')
- const tooltipByElement = new Tooltip(tooltipEl)
-
- expect(tooltipBySelector._element).toEqual(tooltipEl)
- expect(tooltipByElement._element).toEqual(tooltipEl)
- })
-
- it('should not take care of disallowed data attributes', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._config.sanitize).toBeTrue()
- })
-
- it('should convert title and content to string if numbers', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- title: 1,
- content: 7
- })
-
- expect(tooltip._config.title).toEqual('1')
- expect(tooltip._config.content).toEqual('7')
- })
-
- it('should enable selector delegation', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const containerEl = fixtureEl.querySelector('div')
- const tooltipContainer = new Tooltip(containerEl, {
- selector: 'a[rel="tooltip"]',
- trigger: 'click'
- })
-
- containerEl.innerHTML = '
'
-
- const tooltipInContainerEl = containerEl.querySelector('a')
-
- tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- tooltipContainer.dispose()
- resolve()
- })
-
- tooltipInContainerEl.click()
- })
- })
-
- it('should create offset modifier when offset is passed as a function', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
-
- expect(offset).toEqual(jasmine.any(Function))
-
- tooltip.show()
- })
- })
-
- it('should create offset modifier when offset option is passed in data attribute', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._getOffset()).toEqual([10, 20])
- })
-
- it('should allow to pass config to Popper with `popperConfig`', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- popperConfig: {
- placement: 'left'
- }
- })
-
- const popperConfig = tooltip._getPopperConfig('top')
-
- expect(popperConfig.placement).toEqual('left')
- })
-
- it('should allow to pass config to Popper with `popperConfig` as a function', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const getPopperConfig = jasmine.createSpy('getPopperConfig').and.returnValue({ placement: 'left' })
- const tooltip = new Tooltip(tooltipEl, {
- popperConfig: getPopperConfig
- })
-
- const popperConfig = tooltip._getPopperConfig('top')
-
- expect(getPopperConfig).toHaveBeenCalled()
- expect(popperConfig.placement).toEqual('left')
- })
-
- it('should use original title, if not "data-bs-title" is given', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._getTitle()).toEqual('Another tooltip')
- })
- })
-
- describe('enable', () => {
- it('should enable a tooltip', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.enable()
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- resolve()
- })
-
- tooltip.show()
- })
- })
- })
-
- describe('disable', () => {
- it('should disable tooltip', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.disable()
-
- tooltipEl.addEventListener('show.bs.tooltip', () => {
- reject(new Error('should not show a disabled tooltip'))
- })
-
- tooltip.show()
-
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 10)
- })
- })
- })
-
- describe('toggleEnabled', () => {
- it('should toggle enabled', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._isEnabled).toBeTrue()
-
- tooltip.toggleEnabled()
-
- expect(tooltip._isEnabled).toBeFalse()
- })
- })
-
- describe('toggle', () => {
- it('should do nothing if disabled', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.disable()
-
- tooltipEl.addEventListener('show.bs.tooltip', () => {
- reject(new Error('should not show a disabled tooltip'))
- })
-
- tooltip.toggle()
-
- setTimeout(() => {
- expect().nothing()
- resolve()
- }, 10)
- })
- })
-
- it('should show a tooltip', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- resolve()
- })
-
- tooltip.toggle()
- })
- })
-
- it('should call toggle and show the tooltip when trigger is "click"', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- trigger: 'click'
- })
-
- const spy = spyOn(tooltip, 'toggle').and.callThrough()
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(spy).toHaveBeenCalled()
- resolve()
- })
-
- tooltipEl.click()
- })
- })
-
- it('should hide a tooltip', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- })
-
- tooltip.toggle()
- })
- })
-
- it('should call toggle and hide the tooltip when trigger is "click"', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- trigger: 'click'
- })
-
- const spy = spyOn(tooltip, 'toggle').and.callThrough()
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- tooltipEl.click()
- })
-
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(spy).toHaveBeenCalled()
- resolve()
- })
-
- tooltipEl.click()
- })
- })
- })
-
- describe('dispose', () => {
- it('should destroy a tooltip', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const addEventSpy = spyOn(tooltipEl, 'addEventListener').and.callThrough()
- const removeEventSpy = spyOn(tooltipEl, 'removeEventListener').and.callThrough()
-
- const tooltip = new Tooltip(tooltipEl)
-
- expect(Tooltip.getInstance(tooltipEl)).toEqual(tooltip)
-
- const expectedArgs = [
- ['mouseover', jasmine.any(Function), jasmine.any(Boolean)],
- ['mouseout', jasmine.any(Function), jasmine.any(Boolean)],
- ['focusin', jasmine.any(Function), jasmine.any(Boolean)],
- ['focusout', jasmine.any(Function), jasmine.any(Boolean)]
- ]
-
- expect(addEventSpy.calls.allArgs()).toEqual(expectedArgs)
-
- tooltip.dispose()
-
- expect(Tooltip.getInstance(tooltipEl)).toBeNull()
- expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
- })
-
- it('should destroy a tooltip after it is shown and hidden', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should destroy a tooltip and remove it from the dom', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
-
- tooltip.dispose()
-
- expect(document.querySelector('.tooltip')).toBeNull()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should destroy a tooltip and reset it\'s initial title', () => {
- fixtureEl.innerHTML = [
- '
',
- '
'
- ].join('')
-
- const tooltipWithTitleEl = fixtureEl.querySelector('#tooltipWithTitle')
- const tooltip = new Tooltip('#tooltipWithTitle')
- expect(tooltipWithTitleEl.getAttribute('title')).toBeNull()
- tooltip.dispose()
- expect(tooltipWithTitleEl.getAttribute('title')).toBe('tooltipTitle')
-
- const tooltipWithoutTitleEl = fixtureEl.querySelector('#tooltipWithoutTitle')
- const tooltip2 = new Tooltip('#tooltipWithTitle')
- expect(tooltipWithoutTitleEl.getAttribute('title')).toBeNull()
- tooltip2.dispose()
- expect(tooltipWithoutTitleEl.getAttribute('title')).toBeNull()
- })
- })
-
- describe('show', () => {
- it('should show a tooltip', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- 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')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip when hovering a child element', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ''
- ].join('')
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const spy = spyOn(tooltip, 'show')
-
- tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
-
- setTimeout(() => {
- expect(spy).toHaveBeenCalled()
- resolve()
- }, 0)
- })
- })
-
- it('should show a tooltip on mobile', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- document.documentElement.ontouchstart = noop
-
- const spy = spyOn(EventHandler, 'on').and.callThrough()
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- expect(spy).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
- document.documentElement.ontouchstart = undefined
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip relative to placement option', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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('shown.bs.tooltip', () => {
- expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
- expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should not error when trying to show a tooltip that has been removed from the dom', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const firstCallback = () => {
- tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
- let tooltipShown = document.querySelector('.tooltip')
-
- tooltipShown.remove()
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- tooltipShown = document.querySelector('.tooltip')
-
- expect(tooltipShown).not.toBeNull()
- resolve()
- })
-
- tooltip.show()
- }
-
- tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with a dom element container', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- container: fixtureEl
- })
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with a jquery element container', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with a selector in container', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- container: '#fixture'
- })
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with placement as a function', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip without the animation', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- animation: false
- })
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
-
- expect(tip).not.toBeNull()
- expect(tip).not.toHaveClass('fade')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should throw an error the element is not visible', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- try {
- tooltip.show()
- } catch (error) {
- expect(error.message).toEqual('Please use show on visible elements')
- }
- })
-
- it('should not show a tooltip if show.bs.tooltip is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const expectedDone = () => {
- setTimeout(() => {
- expect(document.querySelector('.tooltip')).toBeNull()
- resolve()
- }, 10)
- }
-
- tooltipEl.addEventListener('show.bs.tooltip', ev => {
- ev.preventDefault()
- expectedDone()
- })
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- reject(new Error('Tooltip should not be shown'))
- })
-
- tooltip.show()
- })
- })
-
- it('should show tooltip if leave event hasn\'t occurred before delay expires', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- delay: 150
- })
-
- const spy = spyOn(tooltip, 'show')
-
- setTimeout(() => {
- expect(spy).not.toHaveBeenCalled()
- }, 100)
-
- setTimeout(() => {
- expect(spy).toHaveBeenCalled()
- resolve()
- }, 200)
-
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- })
- })
-
- it('should not show tooltip if leave event occurs before delay expires', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- delay: 150
- })
-
- const spy = spyOn(tooltip, 'show')
-
- setTimeout(() => {
- expect(spy).not.toHaveBeenCalled()
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- }, 100)
-
- setTimeout(() => {
- expect(spy).toHaveBeenCalled()
- expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
- resolve()
- }, 200)
-
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- })
- })
-
- it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._config.delay).toEqual({ show: 0, hide: 150 })
-
- 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.dispatchEvent(createEvent('mouseover'))
- })
- })
-
- it('should not hide tooltip if leave event occurs and interaction remains inside trigger', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '',
- 'Trigger',
- 'the tooltip',
- ''
- ].join('')
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- const triggerChild = tooltipEl.querySelector('b')
-
- const spy = spyOn(tooltip, 'hide').and.callThrough()
-
- tooltipEl.addEventListener('mouseover', () => {
- const moveMouseToChildEvent = createEvent('mouseout')
- Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
- value: triggerChild
- })
-
- tooltipEl.dispatchEvent(moveMouseToChildEvent)
- })
-
- tooltipEl.addEventListener('mouseout', () => {
- expect(spy).not.toHaveBeenCalled()
- resolve()
- })
-
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- })
- })
-
- 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 = '
Trigger'
-
- 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')
- 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'))
- })
- })
-
- it('should only trigger inserted event if a new tooltip element was created', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.15s',
- transitionDelay: '0s'
- })
-
- const insertedFunc = jasmine.createSpy()
- tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
-
- setTimeout(() => {
- expect(insertedFunc).toHaveBeenCalledTimes(1)
- tooltip.hide()
-
- setTimeout(() => {
- tooltip.show()
- }, 100)
-
- setTimeout(() => {
- expect(insertedFunc).toHaveBeenCalledTimes(2)
- resolve()
- }, 200)
- }, 0)
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with custom class provided in data attributes', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with custom class provided as a string in config', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should show a tooltip with custom class provided as a function in config', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should remove `title` attribute if exists', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(tooltipEl.getAttribute('title')).toBeNull()
- resolve()
- })
- tooltip.show()
- })
- })
- })
-
- describe('hide', () => {
- it('should hide a tooltip', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should hide a tooltip on mobile', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- const spy = spyOn(EventHandler, 'off')
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- document.documentElement.ontouchstart = noop
- tooltip.hide()
- })
-
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeNull()
- expect(spy).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
- document.documentElement.ontouchstart = undefined
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should hide a tooltip without animation', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should not hide a tooltip if hide event is prevented', () => {
- return new Promise((resolve, reject) => {
- fixtureEl.innerHTML = '
'
-
- const assertDone = () => {
- setTimeout(() => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- resolve()
- }, 20)
- }
-
- 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', () => {
- reject(new Error('should not trigger hidden event'))
- })
-
- tooltip.show()
- })
- })
-
- it('should not throw error running hide if popper hasn\'t been shown', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(div)
-
- try {
- tooltip.hide()
- expect().nothing()
- } catch {
- throw new Error('should not throw error')
- }
- })
- })
-
- describe('update', () => {
- it('should call popper update', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const spy = spyOn(tooltip._popper, 'update')
-
- tooltip.update()
-
- expect(spy).toHaveBeenCalled()
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should do nothing if the tooltip is not shown', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.update()
- expect().nothing()
- })
- })
-
- describe('_isWithContent', () => {
- it('should return true if there is content', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._isWithContent()).toBeTrue()
- })
-
- it('should return false if there is no content', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._isWithContent()).toBeFalse()
- })
- })
-
- describe('_getTipElement', () => {
- it('should create the tip element and return it', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const spy = spyOn(document, 'createElement').and.callThrough()
-
- expect(tooltip._getTipElement()).toBeDefined()
- expect(spy).toHaveBeenCalled()
- })
-
- it('should return the created tip element', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const spy = spyOn(document, 'createElement').and.callThrough()
-
- expect(tooltip._getTipElement()).toBeDefined()
- expect(spy).toHaveBeenCalled()
-
- spy.calls.reset()
-
- expect(tooltip._getTipElement()).toBeDefined()
- expect(spy).not.toHaveBeenCalled()
- })
- })
-
- describe('setContent', () => {
- it('should set tip content', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, { animation: false })
-
- const tip = tooltip._getTipElement()
-
- tooltip.setContent(tip)
-
- expect(tip).not.toHaveClass('show')
- expect(tip).not.toHaveClass('fade')
- expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
- })
-
- it('should re-show tip if it was already shown', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- tooltip.show()
- const tip = () => tooltip._getTipElement()
-
- expect(tip()).toHaveClass('show')
- tooltip.setContent({ '.tooltip-inner': 'foo' })
-
- expect(tip()).toHaveClass('show')
- expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
- })
-
- it('should keep tip hidden, if it was already hidden before', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- const tip = () => tooltip._getTipElement()
-
- expect(tip()).not.toHaveClass('show')
- tooltip.setContent({ '.tooltip-inner': 'foo' })
-
- expect(tip()).not.toHaveClass('show')
- tooltip.show()
- expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
- })
-
- it('"setContent" should keep the initial template', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.setContent({ '.tooltip-inner': 'foo' })
- const tip = tooltip._getTipElement()
-
- expect(tip).toHaveClass('tooltip')
- expect(tip).toHaveClass('bs-tooltip-auto')
- expect(tip.querySelector('.tooltip-arrow')).not.toBeNull()
- expect(tip.querySelector('.tooltip-inner')).not.toBeNull()
- })
- })
-
- describe('setContent', () => {
- it('should do nothing if the element is null', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.setContent({ '.tooltip': null })
- expect().nothing()
- })
-
- it('should do nothing if the content is a child of the element', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ''
- ].join('')
-
- const tooltipEl = fixtureEl.querySelector('a')
- const childContent = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(tooltipEl, {
- html: true
- })
-
- tooltip._getTipElement().append(childContent)
- tooltip.setContent({ '.tooltip': childContent })
-
- expect().nothing()
- })
-
- it('should add the content as a child of the element for jQuery elements', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ''
- ].join('')
-
- const tooltipEl = fixtureEl.querySelector('a')
- const childContent = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(tooltipEl, {
- html: true
- })
-
- tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } })
- tooltip.show()
-
- expect(childContent.parentNode).toEqual(tooltip._getTipElement())
- })
-
- it('should add the child text content in the element', () => {
- fixtureEl.innerHTML = [
- '
',
- ' Tooltip
',
- ''
- ].join('')
-
- const tooltipEl = fixtureEl.querySelector('a')
- const childContent = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.setContent({ '.tooltip': childContent })
-
- expect(childContent.textContent).toEqual(tooltip._getTipElement().textContent)
- })
-
- it('should add html without sanitize it', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- sanitize: false,
- html: true
- })
-
- tooltip.setContent({ '.tooltip': '
Tooltip
' })
-
- expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
- })
-
- it('should add html sanitized', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- html: true
- })
-
- const content = [
- '
',
- ' ',
- '
'
- ].join('')
-
- tooltip.setContent({ '.tooltip': content })
- expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
- expect(tooltip._getTipElement().querySelector('button')).toBeNull()
- })
-
- it('should add text content', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltip.setContent({ '.tooltip': 'test' })
-
- expect(tooltip._getTipElement().textContent).toEqual('test')
- })
- })
-
- describe('_getTitle', () => {
- it('should return the title', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- expect(tooltip._getTitle()).toEqual('Another tooltip')
- })
-
- it('should call title function', () => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- title: () => 'test'
- })
-
- expect(tooltip._getTitle()).toEqual('test')
- })
- })
-
- describe('getInstance', () => {
- it('should return tooltip instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const alert = new Tooltip(div)
-
- expect(Tooltip.getInstance(div)).toEqual(alert)
- expect(Tooltip.getInstance(div)).toBeInstanceOf(Tooltip)
- })
-
- it('should return null when there is no tooltip instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Tooltip.getInstance(div)).toBeNull()
- })
- })
-
- describe('aria-label', () => {
- it('should add the aria-label attribute for referencing original title', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
-
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should add the aria-label attribute when element text content is a whitespace string', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
-
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toEqual('A tooltip')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should not add the aria-label attribute if the attribute already exists', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
-
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
- resolve()
- })
-
- tooltip.show()
- })
- })
-
- it('should not add the aria-label attribute if the element has text content', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
text content'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
-
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toBeNull()
- resolve()
- })
-
- tooltip.show()
- })
- })
- })
-
- describe('getOrCreateInstance', () => {
- it('should return tooltip instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(div)
-
- expect(Tooltip.getOrCreateInstance(div)).toEqual(tooltip)
- expect(Tooltip.getInstance(div)).toEqual(Tooltip.getOrCreateInstance(div, {}))
- expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
- })
-
- it('should return new instance when there is no tooltip instance', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Tooltip.getInstance(div)).toBeNull()
- expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
- })
-
- it('should return new instance when there is no tooltip instance with given configuration', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Tooltip.getInstance(div)).toBeNull()
- const tooltip = Tooltip.getOrCreateInstance(div, {
- title: () => 'test'
- })
- expect(tooltip).toBeInstanceOf(Tooltip)
-
- expect(tooltip._getTitle()).toEqual('test')
- })
-
- it('should return the instance when exists without given configuration', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(div, {
- title: () => 'nothing'
- })
- expect(Tooltip.getInstance(div)).toEqual(tooltip)
-
- const tooltip2 = Tooltip.getOrCreateInstance(div, {
- title: () => 'test'
- })
- expect(tooltip).toBeInstanceOf(Tooltip)
- expect(tooltip2).toEqual(tooltip)
-
- expect(tooltip2._getTitle()).toEqual('nothing')
- })
- })
-
- describe('jQueryInterface', () => {
- it('should create a tooltip', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- jQueryMock.fn.tooltip = Tooltip.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tooltip.call(jQueryMock)
-
- expect(Tooltip.getInstance(div)).not.toBeNull()
- })
-
- it('should not re create a tooltip', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(div)
-
- jQueryMock.fn.tooltip = Tooltip.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tooltip.call(jQueryMock)
-
- expect(Tooltip.getInstance(div)).toEqual(tooltip)
- })
-
- it('should call a tooltip method', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const tooltip = new Tooltip(div)
-
- const spy = spyOn(tooltip, 'show')
-
- jQueryMock.fn.tooltip = Tooltip.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tooltip.call(jQueryMock, 'show')
-
- expect(Tooltip.getInstance(div)).toEqual(tooltip)
- expect(spy).toHaveBeenCalled()
- })
-
- it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const action = 'undefinedMethod'
-
- jQueryMock.fn.tooltip = Tooltip.jQueryInterface
- jQueryMock.elements = [div]
-
- expect(() => {
- jQueryMock.fn.tooltip.call(jQueryMock, action)
- }).toThrowError(TypeError, `No method named "${action}"`)
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/backdrop.spec.js b/bootstrap/js/tests/unit/util/backdrop.spec.js
deleted file mode 100644
index 0faaac6..0000000
--- a/bootstrap/js/tests/unit/util/backdrop.spec.js
+++ /dev/null
@@ -1,321 +0,0 @@
-import Backdrop from '../../../src/util/backdrop.js'
-import { getTransitionDurationFromElement } from '../../../src/util/index.js'
-import { clearFixture, getFixture } from '../../helpers/fixture.js'
-
-const CLASS_BACKDROP = '.modal-backdrop'
-const CLASS_NAME_FADE = 'fade'
-const CLASS_NAME_SHOW = 'show'
-
-describe('Backdrop', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- const list = document.querySelectorAll(CLASS_BACKDROP)
-
- for (const el of list) {
- el.remove()
- }
- })
-
- describe('show', () => {
- 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)
-
- instance.show()
- instance.show(() => {
- expect(getElements()).toHaveSize(1)
- for (const el of getElements()) {
- expect(el).toHaveClass(CLASS_NAME_SHOW)
- }
-
- resolve()
- })
- })
- })
-
- 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)
- resolve()
- })
- })
- })
-
- 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)
-
- instance.show(() => {
- expect(getElements()).toHaveSize(1)
- for (const el of getElements()) {
- expect(el).toHaveClass(CLASS_NAME_FADE)
- }
-
- resolve()
- })
- })
- })
- })
-
- describe('hide', () => {
- 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)
-
- expect(getElements()).toHaveSize(0)
- instance.show(() => {
- expect(getElements()).toHaveSize(1)
- instance.hide(() => {
- expect(getElements()).toHaveSize(0)
- resolve()
- })
- })
- })
- })
-
- 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)
- resolve()
- })
- })
- })
-
- 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()
- resolve()
- })
- })
- })
- })
-
- it('should not error if the backdrop no longer has a parent', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const wrapper = fixtureEl.querySelector('#wrapper')
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true,
- rootElement: wrapper
- })
-
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
-
- instance.show(() => {
- wrapper.remove()
- instance.hide(() => {
- expect(getElements()).toHaveSize(0)
- resolve()
- })
- })
- })
- })
- })
-
- describe('click callback', () => {
- 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()
- resolve()
- }, 10)
- }
-
- instance.show(() => {
- const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
- document.querySelector(CLASS_BACKDROP).dispatchEvent(clickEvent)
- endTest()
- })
- })
- })
-
- 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()
- })
- })
-
- 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)
- })
- })
-
- 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', () => {
- 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', () => {
- 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', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- 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', () => {
- 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()
- })
- })
- })
- })
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/component-functions.spec.js b/bootstrap/js/tests/unit/util/component-functions.spec.js
deleted file mode 100644
index ce83785..0000000
--- a/bootstrap/js/tests/unit/util/component-functions.spec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import BaseComponent from '../../../src/base-component.js'
-import { enableDismissTrigger } from '../../../src/util/component-functions.js'
-import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
-
-class DummyClass2 extends BaseComponent {
- static get NAME() {
- return 'test'
- }
-
- hide() {
- return true
- }
-
- testMethod() {
- return true
- }
-}
-
-describe('Plugin functions', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- })
-
- describe('data-bs-dismiss functionality', () => {
- it('should get Plugin and execute the given method, when a click occurred on data-bs-dismiss="PluginName"', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- '
'
- ].join('')
-
- const spyGet = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
- const spyTest = spyOn(DummyClass2.prototype, 'testMethod')
- const componentWrapper = fixtureEl.querySelector('#foo')
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
- const event = createEvent('click')
-
- enableDismissTrigger(DummyClass2, 'testMethod')
- btnClose.dispatchEvent(event)
-
- expect(spyGet).toHaveBeenCalledWith(componentWrapper)
- expect(spyTest).toHaveBeenCalled()
- })
-
- it('if data-bs-dismiss="PluginName" hasn\'t got "data-bs-target", "getOrCreateInstance" has to be initialized by closest "plugin.Name" class', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- '
'
- ].join('')
-
- const spyGet = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
- const spyHide = spyOn(DummyClass2.prototype, 'hide')
- const componentWrapper = fixtureEl.querySelector('#foo')
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
- const event = createEvent('click')
-
- enableDismissTrigger(DummyClass2)
- btnClose.dispatchEvent(event)
-
- expect(spyGet).toHaveBeenCalledWith(componentWrapper)
- expect(spyHide).toHaveBeenCalled()
- })
-
- it('if data-bs-dismiss="PluginName" is disabled, must not trigger function', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- '
'
- ].join('')
-
- const spy = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
- const event = createEvent('click')
-
- enableDismissTrigger(DummyClass2)
- btnClose.dispatchEvent(event)
-
- expect(spy).not.toHaveBeenCalled()
- })
-
- it('should prevent default when the trigger is
or ', () => {
- fixtureEl.innerHTML = [
- ''
- ].join('')
-
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
- const event = createEvent('click')
-
- enableDismissTrigger(DummyClass2)
- const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
- btnClose.dispatchEvent(event)
-
- expect(spy).toHaveBeenCalled()
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/config.spec.js b/bootstrap/js/tests/unit/util/config.spec.js
deleted file mode 100644
index 93987a7..0000000
--- a/bootstrap/js/tests/unit/util/config.spec.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import Config from '../../../src/util/config.js'
-import { clearFixture, getFixture } from '../../helpers/fixture.js'
-
-class DummyConfigClass extends Config {
- static get NAME() {
- return 'dummy'
- }
-}
-
-describe('Config', () => {
- let fixtureEl
- const name = 'dummy'
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- })
-
- describe('NAME', () => {
- it('should return plugin NAME', () => {
- expect(DummyConfigClass.NAME).toEqual(name)
- })
- })
-
- describe('DefaultType', () => {
- it('should return plugin default type', () => {
- expect(DummyConfigClass.DefaultType).toEqual(jasmine.any(Object))
- })
- })
-
- describe('Default', () => {
- it('should return plugin defaults', () => {
- expect(DummyConfigClass.Default).toEqual(jasmine.any(Object))
- })
- })
-
- describe('mergeConfigObj', () => {
- it('should parse element\'s data attributes and merge it with default config. Element\'s data attributes must excel Defaults', () => {
- fixtureEl.innerHTML = ''
-
- spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
- testBool: true,
- testString: 'foo',
- testString1: 'foo',
- testInt: 7
- })
- const instance = new DummyConfigClass()
- const configResult = instance._mergeConfigObj({}, fixtureEl.querySelector('#test'))
-
- expect(configResult.testBool).toEqual(false)
- expect(configResult.testString).toEqual('foo')
- expect(configResult.testString1).toEqual('bar')
- expect(configResult.testInt).toEqual(8)
- })
-
- it('should parse element\'s data attributes and merge it with default config, plug these given during method call. The programmatically given should excel all', () => {
- fixtureEl.innerHTML = ''
-
- spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
- testBool: true,
- testString: 'foo',
- testString1: 'foo',
- testInt: 7
- })
- const instance = new DummyConfigClass()
- const configResult = instance._mergeConfigObj({
- testString1: 'test',
- testInt: 3
- }, fixtureEl.querySelector('#test'))
-
- expect(configResult.testBool).toEqual(false)
- expect(configResult.testString).toEqual('foo')
- expect(configResult.testString1).toEqual('test')
- expect(configResult.testInt).toEqual(3)
- })
-
- it('should parse element\'s data attribute `config` and any rest attributes. The programmatically given should excel all. Data attribute `config` should excel only Defaults', () => {
- fixtureEl.innerHTML = ''
-
- spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
- testBool: true,
- testString: 'foo',
- testString1: 'foo',
- testInt: 7,
- testInt2: 600
- })
- const instance = new DummyConfigClass()
- const configResult = instance._mergeConfigObj({
- testString1: 'test'
- }, fixtureEl.querySelector('#test'))
-
- expect(configResult.testBool).toEqual(false)
- expect(configResult.testString).toEqual('foo')
- expect(configResult.testString1).toEqual('test')
- expect(configResult.testInt).toEqual(8)
- expect(configResult.testInt2).toEqual(100)
- })
-
- it('should omit element\'s data attribute `config` if is not an object', () => {
- fixtureEl.innerHTML = ''
-
- spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
- testInt: 7,
- testInt2: 79
- })
- const instance = new DummyConfigClass()
- const configResult = instance._mergeConfigObj({}, fixtureEl.querySelector('#test'))
-
- expect(configResult.testInt).toEqual(8)
- expect(configResult.testInt2).toEqual(79)
- })
- })
-
- describe('typeCheckConfig', () => {
- it('should check type of the config object', () => {
- spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
- toggle: 'boolean',
- parent: '(string|element)'
- })
- const config = {
- toggle: true,
- parent: 777
- }
-
- const obj = new DummyConfigClass()
- expect(() => {
- obj._typeCheckConfig(config)
- }).toThrowError(TypeError, `${obj.constructor.NAME.toUpperCase()}: Option "parent" provided type "number" but expected type "(string|element)".`)
- })
-
- it('should return null stringified when null is passed', () => {
- spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
- toggle: 'boolean',
- parent: '(null|element)'
- })
-
- const obj = new DummyConfigClass()
- const config = {
- toggle: true,
- parent: null
- }
-
- obj._typeCheckConfig(config)
- expect().nothing()
- })
-
- it('should return undefined stringified when undefined is passed', () => {
- spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
- toggle: 'boolean',
- parent: '(undefined|element)'
- })
-
- const obj = new DummyConfigClass()
- const config = {
- toggle: true,
- parent: undefined
- }
-
- obj._typeCheckConfig(config)
- expect().nothing()
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/focustrap.spec.js b/bootstrap/js/tests/unit/util/focustrap.spec.js
deleted file mode 100644
index 0a20017..0000000
--- a/bootstrap/js/tests/unit/util/focustrap.spec.js
+++ /dev/null
@@ -1,218 +0,0 @@
-import EventHandler from '../../../src/dom/event-handler.js'
-import SelectorEngine from '../../../src/dom/selector-engine.js'
-import FocusTrap from '../../../src/util/focustrap.js'
-import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
-
-describe('FocusTrap', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- })
-
- describe('activate', () => {
- it('should autofocus itself by default', () => {
- fixtureEl.innerHTML = ''
-
- const trapElement = fixtureEl.querySelector('div')
-
- const spy = spyOn(trapElement, 'focus')
-
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
-
- expect(spy).toHaveBeenCalled()
- })
-
- it('if configured not to autofocus, should not autofocus itself', () => {
- fixtureEl.innerHTML = ''
-
- const trapElement = fixtureEl.querySelector('div')
-
- const spy = spyOn(trapElement, 'focus')
-
- const focustrap = new FocusTrap({ trapElement, autofocus: false })
- focustrap.activate()
-
- expect(spy).not.toHaveBeenCalled()
- })
-
- it('should force focus inside focus trap if it can', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- 'outside',
- '
'
- ].join('')
-
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
-
- const inside = document.getElementById('inside')
-
- const focusInListener = () => {
- expect(spy).toHaveBeenCalled()
- document.removeEventListener('focusin', focusInListener)
- resolve()
- }
-
- const spy = spyOn(inside, 'focus')
- spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside])
-
- document.addEventListener('focusin', focusInListener)
-
- const focusInEvent = createEvent('focusin', { bubbles: true })
- Object.defineProperty(focusInEvent, 'target', {
- value: document.getElementById('outside')
- })
-
- document.dispatchEvent(focusInEvent)
- })
- })
-
- it('should wrap focus around forward on tab', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
outside',
- '
'
- ].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])
- const spy = spyOn(first, 'focus').and.callThrough()
-
- const focusInListener = () => {
- expect(spy).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', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
outside',
- '
'
- ].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])
- const spy = spyOn(last, 'focus').and.callThrough()
-
- const focusInListener = () => {
- expect(spy).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', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
outside',
- '
'
- ].join('')
-
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
-
- const focusInListener = () => {
- expect(spy).toHaveBeenCalled()
- document.removeEventListener('focusin', focusInListener)
- resolve()
- }
-
- const spy = spyOn(focustrap._config.trapElement, 'focus')
-
- document.addEventListener('focusin', focusInListener)
-
- const focusInEvent = createEvent('focusin', { bubbles: true })
- Object.defineProperty(focusInEvent, 'target', {
- value: document.getElementById('outside')
- })
-
- document.dispatchEvent(focusInEvent)
- })
- })
- })
-
- describe('deactivate', () => {
- it('should flag itself as no longer active', () => {
- const focustrap = new FocusTrap({ trapElement: fixtureEl })
- focustrap.activate()
- expect(focustrap._isActive).toBeTrue()
-
- focustrap.deactivate()
- expect(focustrap._isActive).toBeFalse()
- })
-
- it('should remove all event listeners', () => {
- const focustrap = new FocusTrap({ trapElement: fixtureEl })
- focustrap.activate()
-
- const spy = spyOn(EventHandler, 'off')
- focustrap.deactivate()
-
- expect(spy).toHaveBeenCalled()
- })
-
- it('doesn\'t try removing event listeners unless it needs to (in case it hasn\'t been activated)', () => {
- const focustrap = new FocusTrap({ trapElement: fixtureEl })
-
- const spy = spyOn(EventHandler, 'off')
- focustrap.deactivate()
-
- expect(spy).not.toHaveBeenCalled()
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/index.spec.js b/bootstrap/js/tests/unit/util/index.spec.js
deleted file mode 100644
index 4065a91..0000000
--- a/bootstrap/js/tests/unit/util/index.spec.js
+++ /dev/null
@@ -1,720 +0,0 @@
-import * as Util from '../../../src/util/index.js'
-import { noop } from '../../../src/util/index.js'
-import { clearFixture, getFixture } from '../../helpers/fixture.js'
-
-describe('Util', () => {
- let fixtureEl
-
- beforeAll(() => {
- fixtureEl = getFixture()
- })
-
- afterEach(() => {
- clearFixture()
- })
-
- describe('getUID', () => {
- it('should generate uid', () => {
- const uid = Util.getUID('bs')
- const uid2 = Util.getUID('bs')
-
- expect(uid).not.toEqual(uid2)
- })
- })
-
- describe('getTransitionDurationFromElement', () => {
- it('should get transition from element', () => {
- fixtureEl.innerHTML = '
'
-
- expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(300)
- })
-
- it('should return 0 if the element is undefined or null', () => {
- expect(Util.getTransitionDurationFromElement(null)).toEqual(0)
- expect(Util.getTransitionDurationFromElement(undefined)).toEqual(0)
- })
-
- it('should return 0 if the element do not possess transition', () => {
- fixtureEl.innerHTML = '
'
-
- expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(0)
- })
- })
-
- describe('triggerTransitionEnd', () => {
- it('should trigger transitionend event', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = '
'
-
- const el = fixtureEl.querySelector('div')
- const spy = spyOn(el, 'dispatchEvent').and.callThrough()
-
- el.addEventListener('transitionend', () => {
- expect(spy).toHaveBeenCalled()
- resolve()
- })
-
- Util.triggerTransitionEnd(el)
- })
- })
- })
-
- describe('isElement', () => {
- it('should detect if the parameter is an element or not and return Boolean', () => {
- fixtureEl.innerHTML = [
- '
',
- '
'
- ].join('')
-
- const el = fixtureEl.querySelector('#foo')
-
- expect(Util.isElement(el)).toBeTrue()
- expect(Util.isElement({})).toBeFalse()
- expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toBeFalse()
- })
-
- it('should detect jQuery element', () => {
- fixtureEl.innerHTML = '
'
-
- const el = fixtureEl.querySelector('div')
- const fakejQuery = {
- 0: el,
- jquery: 'foo'
- }
-
- expect(Util.isElement(fakejQuery)).toBeTrue()
- })
- })
-
- describe('getElement', () => {
- it('should try to parse element', () => {
- fixtureEl.innerHTML = [
- '
',
- '
'
- ].join('')
-
- const el = fixtureEl.querySelector('div')
-
- expect(Util.getElement(el)).toEqual(el)
- expect(Util.getElement('#foo')).toEqual(el)
- expect(Util.getElement('#fail')).toBeNull()
- expect(Util.getElement({})).toBeNull()
- expect(Util.getElement([])).toBeNull()
- expect(Util.getElement()).toBeNull()
- expect(Util.getElement(null)).toBeNull()
- expect(Util.getElement(fixtureEl.querySelectorAll('.test'))).toBeNull()
-
- const fakejQueryObject = {
- 0: el,
- jquery: 'foo'
- }
-
- expect(Util.getElement(fakejQueryObject)).toEqual(el)
- })
- })
-
- describe('isVisible', () => {
- it('should return false if the element is not defined', () => {
- expect(Util.isVisible(null)).toBeFalse()
- expect(Util.isVisible(undefined)).toBeFalse()
- })
-
- it('should return false if the element provided is not a dom element', () => {
- expect(Util.isVisible({})).toBeFalse()
- })
-
- it('should return false if the element is not visible with display none', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Util.isVisible(div)).toBeFalse()
- })
-
- it('should return false if the element is not visible with visibility hidden', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- expect(Util.isVisible(div)).toBeFalse()
- })
-
- it('should return false if an ancestor element is display none', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('.content')
-
- expect(Util.isVisible(div)).toBeFalse()
- })
-
- it('should return false if an ancestor element is visibility hidden', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('.content')
-
- expect(Util.isVisible(div)).toBeFalse()
- })
-
- it('should return true if an ancestor element is visibility hidden, but reverted', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('.content')
-
- expect(Util.isVisible(div)).toBeTrue()
- })
-
- it('should return true if the element is visible', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
-
- expect(Util.isVisible(div)).toBeTrue()
- })
-
- it('should return false if the element is hidden, but not via display or visibility', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' '
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
-
- expect(Util.isVisible(div)).toBeFalse()
- })
-
- it('should return true if its a closed details element', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('#element')
-
- expect(Util.isVisible(div)).toBeTrue()
- })
-
- it('should return true if the element is visible inside an open details element', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' '
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
-
- expect(Util.isVisible(div)).toBeTrue()
- })
-
- it('should return true if the element is a visible summary in a closed details element', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- '
',
- ' '
- ].join('')
-
- const element1 = fixtureEl.querySelector('#element-1')
- const element2 = fixtureEl.querySelector('#element-2')
-
- expect(Util.isVisible(element1)).toBeTrue()
- expect(Util.isVisible(element2)).toBeTrue()
- })
- })
-
- describe('isDisabled', () => {
- it('should return true if the element is not defined', () => {
- expect(Util.isDisabled(null)).toBeTrue()
- expect(Util.isDisabled(undefined)).toBeTrue()
- expect(Util.isDisabled()).toBeTrue()
- })
-
- it('should return true if the element provided is not a dom element', () => {
- expect(Util.isDisabled({})).toBeTrue()
- expect(Util.isDisabled('test')).toBeTrue()
- })
-
- it('should return true if the element has disabled attribute', () => {
- fixtureEl.innerHTML = [
- '
',
- '
',
- '
',
- '
',
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
- const div1 = fixtureEl.querySelector('#element1')
- const div2 = fixtureEl.querySelector('#element2')
-
- expect(Util.isDisabled(div)).toBeTrue()
- expect(Util.isDisabled(div1)).toBeTrue()
- expect(Util.isDisabled(div2)).toBeTrue()
- })
-
- it('should return false if the element has disabled attribute with "false" value, or doesn\'t have attribute', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
- const div1 = fixtureEl.querySelector('#element1')
-
- expect(Util.isDisabled(div)).toBeFalse()
- expect(Util.isDisabled(div1)).toBeFalse()
- })
-
- it('should return false if the element is not disabled ', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const el = selector => fixtureEl.querySelector(selector)
-
- expect(Util.isDisabled(el('#button'))).toBeFalse()
- expect(Util.isDisabled(el('#select'))).toBeFalse()
- expect(Util.isDisabled(el('#input'))).toBeFalse()
- })
-
- it('should return true if the element has disabled attribute', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- ' ',
- ' ',
- ' ',
- ' ',
- ' ',
- ' ',
- '
'
- ].join('')
-
- const el = selector => fixtureEl.querySelector(selector)
-
- expect(Util.isDisabled(el('#input'))).toBeTrue()
- expect(Util.isDisabled(el('#input1'))).toBeTrue()
- expect(Util.isDisabled(el('#button'))).toBeTrue()
- expect(Util.isDisabled(el('#button1'))).toBeTrue()
- expect(Util.isDisabled(el('#button2'))).toBeTrue()
- expect(Util.isDisabled(el('#input'))).toBeTrue()
- })
-
- it('should return true if the element has class "disabled"', () => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('#element')
-
- expect(Util.isDisabled(div)).toBeTrue()
- })
-
- it('should return true if the element has class "disabled" but disabled attribute is false', () => {
- fixtureEl.innerHTML = [
- '
',
- ' ',
- '
'
- ].join('')
-
- const div = fixtureEl.querySelector('#input')
-
- expect(Util.isDisabled(div)).toBeTrue()
- })
- })
-
- describe('findShadowRoot', () => {
- it('should return null if shadow dom is not available', () => {
- // Only for newer browsers
- if (!document.documentElement.attachShadow) {
- expect().nothing()
- return
- }
-
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
-
- spyOn(document.documentElement, 'attachShadow').and.returnValue(null)
-
- expect(Util.findShadowRoot(div)).toBeNull()
- })
-
- it('should return null when we do not find a shadow root', () => {
- // Only for newer browsers
- if (!document.documentElement.attachShadow) {
- expect().nothing()
- return
- }
-
- spyOn(document, 'getRootNode').and.returnValue(undefined)
-
- expect(Util.findShadowRoot(document)).toBeNull()
- })
-
- it('should return the shadow root when found', () => {
- // Only for newer browsers
- if (!document.documentElement.attachShadow) {
- expect().nothing()
- return
- }
-
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const shadowRoot = div.attachShadow({
- mode: 'open'
- })
-
- expect(Util.findShadowRoot(shadowRoot)).toEqual(shadowRoot)
-
- shadowRoot.innerHTML = '
'
-
- expect(Util.findShadowRoot(shadowRoot.firstChild)).toEqual(shadowRoot)
- })
- })
-
- describe('noop', () => {
- it('should be a function', () => {
- expect(Util.noop).toEqual(jasmine.any(Function))
- })
- })
-
- describe('reflow', () => {
- it('should return element offset height to force the reflow', () => {
- fixtureEl.innerHTML = '
'
-
- const div = fixtureEl.querySelector('div')
- const spy = spyOnProperty(div, 'offsetHeight')
- Util.reflow(div)
- expect(spy).toHaveBeenCalled()
- })
- })
-
- describe('getjQuery', () => {
- const fakejQuery = { trigger() {} }
-
- beforeEach(() => {
- Object.defineProperty(window, 'jQuery', {
- value: fakejQuery,
- writable: true
- })
- })
-
- afterEach(() => {
- window.jQuery = undefined
- })
-
- it('should return jQuery object when present', () => {
- expect(Util.getjQuery()).toEqual(fakejQuery)
- })
-
- it('should not return jQuery object when present if data-bs-no-jquery', () => {
- document.body.setAttribute('data-bs-no-jquery', '')
-
- expect(window.jQuery).toEqual(fakejQuery)
- expect(Util.getjQuery()).toBeNull()
-
- document.body.removeAttribute('data-bs-no-jquery')
- })
-
- it('should not return jQuery if not present', () => {
- window.jQuery = undefined
- expect(Util.getjQuery()).toBeNull()
- })
- })
-
- describe('onDOMContentLoaded', () => {
- it('should execute callbacks when DOMContentLoaded is fired and should not add more than one listener', () => {
- const spy = jasmine.createSpy()
- const spy2 = jasmine.createSpy()
-
- const spyAdd = spyOn(document, 'addEventListener').and.callThrough()
- spyOnProperty(document, 'readyState').and.returnValue('loading')
-
- Util.onDOMContentLoaded(spy)
- Util.onDOMContentLoaded(spy2)
-
- document.dispatchEvent(new Event('DOMContentLoaded', {
- bubbles: true,
- cancelable: true
- }))
-
- expect(spy).toHaveBeenCalled()
- expect(spy2).toHaveBeenCalled()
- expect(spyAdd).toHaveBeenCalledTimes(1)
- })
-
- it('should execute callback if readyState is not "loading"', () => {
- const spy = jasmine.createSpy()
- Util.onDOMContentLoaded(spy)
- expect(spy).toHaveBeenCalled()
- })
- })
-
- describe('defineJQueryPlugin', () => {
- const fakejQuery = { fn: {} }
-
- beforeEach(() => {
- Object.defineProperty(window, 'jQuery', {
- value: fakejQuery,
- writable: true
- })
- })
-
- afterEach(() => {
- window.jQuery = undefined
- })
-
- it('should define a plugin on the jQuery instance', () => {
- const pluginMock = Util.noop
- pluginMock.NAME = 'test'
- pluginMock.jQueryInterface = Util.noop
-
- Util.defineJQueryPlugin(pluginMock)
- expect(fakejQuery.fn.test).toEqual(pluginMock.jQueryInterface)
- expect(fakejQuery.fn.test.Constructor).toEqual(pluginMock)
- expect(fakejQuery.fn.test.noConflict).toEqual(jasmine.any(Function))
- })
- })
-
- describe('execute', () => {
- it('should execute if arg is function', () => {
- const spy = jasmine.createSpy('spy')
- Util.execute(spy)
- expect(spy).toHaveBeenCalled()
- })
-
- it('should execute if arg is function & return the result', () => {
- const functionFoo = (num1, num2 = 10) => num1 + num2
- const resultFoo = Util.execute(functionFoo, [4, 5])
- expect(resultFoo).toBe(9)
-
- const resultFoo1 = Util.execute(functionFoo, [4])
- expect(resultFoo1).toBe(14)
-
- const functionBar = () => 'foo'
- const resultBar = Util.execute(functionBar)
- expect(resultBar).toBe('foo')
- })
-
- it('should not execute if arg is not function & return default argument', () => {
- const foo = 'bar'
- expect(Util.execute(foo)).toBe('bar')
- expect(Util.execute(foo, [], 4)).toBe(4)
- })
- })
-
- describe('executeAfterTransition', () => {
- it('should immediately execute a function when waitForTransition parameter is false', () => {
- const el = document.createElement('div')
- const callbackSpy = jasmine.createSpy('callback spy')
- const eventListenerSpy = spyOn(el, 'addEventListener')
-
- Util.executeAfterTransition(callbackSpy, el, false)
-
- expect(callbackSpy).toHaveBeenCalled()
- expect(eventListenerSpy).not.toHaveBeenCalled()
- })
-
- it('should execute a function when a transitionend event is dispatched', () => {
- const el = document.createElement('div')
- const callbackSpy = jasmine.createSpy('callback spy')
-
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
-
- Util.executeAfterTransition(callbackSpy, el)
-
- el.dispatchEvent(new TransitionEvent('transitionend'))
-
- expect(callbackSpy).toHaveBeenCalled()
- })
-
- it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', () => {
- 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'
- })
-
- Util.executeAfterTransition(callbackSpy, el)
-
- 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', () => {
- 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'
- })
-
- Util.executeAfterTransition(callbackSpy, el)
-
- setTimeout(() => {
- el.dispatchEvent(new TransitionEvent('transitionend'))
- }, 50)
-
- setTimeout(() => {
- expect(callbackSpy).toHaveBeenCalledTimes(1)
- resolve()
- }, 70)
- })
- })
-
- 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'
- })
-
- Util.executeAfterTransition(noop, el)
-
- // simulate a event dispatched by the browser
- el.dispatchEvent(new TransitionEvent('transitionend'))
-
- const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
-
- setTimeout(() => {
- // setTimeout should not have triggered another transitionend event.
- expect(dispatchSpy).not.toHaveBeenCalled()
- resolve()
- }, 70)
- })
- })
-
- it('should ignore transitionend events from nested elements', () => {
- return new Promise(resolve => {
- fixtureEl.innerHTML = [
- '
'
- ].join('')
-
- const outer = fixtureEl.querySelector('.outer')
- const nested = fixtureEl.querySelector('.nested')
- const callbackSpy = jasmine.createSpy('callback spy')
-
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
-
- Util.executeAfterTransition(callbackSpy, outer)
-
- nested.dispatchEvent(new TransitionEvent('transitionend', {
- bubbles: true
- }))
-
- setTimeout(() => {
- expect(callbackSpy).not.toHaveBeenCalled()
- }, 20)
-
- setTimeout(() => {
- expect(callbackSpy).toHaveBeenCalled()
- resolve()
- }, 70)
- })
- })
- })
-
- describe('getNextActiveElement', () => {
- it('should return first element if active not exists or not given and shouldGetNext is either true, or false with cycling being disabled', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, '', true, true)).toEqual('a')
- expect(Util.getNextActiveElement(array, 'g', true, true)).toEqual('a')
- expect(Util.getNextActiveElement(array, '', true, false)).toEqual('a')
- expect(Util.getNextActiveElement(array, 'g', true, false)).toEqual('a')
- expect(Util.getNextActiveElement(array, '', false, false)).toEqual('a')
- expect(Util.getNextActiveElement(array, 'g', false, false)).toEqual('a')
- })
-
- it('should return last element if active not exists or not given and shouldGetNext is false but cycling is enabled', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, '', false, true)).toEqual('d')
- expect(Util.getNextActiveElement(array, 'g', false, true)).toEqual('d')
- })
-
- it('should return next element or same if is last', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, 'a', true, true)).toEqual('b')
- expect(Util.getNextActiveElement(array, 'b', true, true)).toEqual('c')
- expect(Util.getNextActiveElement(array, 'd', true, false)).toEqual('d')
- })
-
- it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, 'c', true, true)).toEqual('d')
- expect(Util.getNextActiveElement(array, 'd', true, true)).toEqual('a')
- })
-
- it('should return previous element or same if is first', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, 'b', false, true)).toEqual('a')
- expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
- expect(Util.getNextActiveElement(array, 'a', false, false)).toEqual('a')
- })
-
- it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
- const array = ['a', 'b', 'c', 'd']
-
- expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
- expect(Util.getNextActiveElement(array, 'a', false, true)).toEqual('d')
- })
- })
-})
diff --git a/bootstrap/js/tests/unit/util/sanitizer.spec.js b/bootstrap/js/tests/unit/util/sanitizer.spec.js
deleted file mode 100644
index 2b21ef2..0000000
--- a/bootstrap/js/tests/unit/util/sanitizer.spec.js
+++ /dev/null
@@ -1,163 +0,0 @@
-import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
-
-describe('Sanitizer', () => {
- describe('sanitizeHtml', () => {
- it('should return the same on empty string', () => {
- const empty = ''
-
- const result = sanitizeHtml(empty, DefaultAllowlist, null)
-
- expect(result).toEqual(empty)
- })
-
- it('should retain tags with valid URLs', () => {
- const validUrls = [
- '',
- 'http://abc',
- 'HTTP://abc',
- 'https://abc',
- 'HTTPS://abc',
- 'ftp://abc',
- 'FTP://abc',
- 'mailto:me@example.com',
- 'MAILTO:me@example.com',
- 'tel:123-123-1234',
- 'TEL:123-123-1234',
- 'sip:me@example.com',
- 'SIP:me@example.com',
- '#anchor',
- '/page1.md',
- 'http://JavaScript/my.js',
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', // Truncated.
- 'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
- 'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
- 'unknown-scheme:abc'
- ]
-
- for (const url of validUrls) {
- const template = [
- '
',
- `
Click me`,
- '
Some content',
- '
'
- ].join('')
-
- const result = sanitizeHtml(template, DefaultAllowlist, null)
-
- expect(result).toContain(`href="${url}"`)
- }
- })
-
- it('should sanitize template by removing tags with XSS', () => {
- const invalidUrls = [
- // eslint-disable-next-line no-script-url
- 'javascript:alert(7)',
- // eslint-disable-next-line no-script-url
- 'javascript:evil()',
- // eslint-disable-next-line no-script-url
- 'JavaScript:abc',
- ' javascript:abc',
- ' \n Java\n Script:abc',
- 'javascript:',
- 'javascript:',
- 'j avascript:',
- 'javascript:',
- 'javascript:',
- 'jav ascript:alert();',
- 'jav\u0000ascript:alert();'
- ]
-
- for (const url of invalidUrls) {
- const template = [
- '
',
- `
Click me`,
- '
Some content',
- '
'
- ].join('')
-
- const result = sanitizeHtml(template, DefaultAllowlist, null)
-
- expect(result).not.toContain(`href="${url}"`)
- }
- })
-
- it('should sanitize template and work with multiple regex', () => {
- const template = [
- '
',
- '
Click me',
- '
Some content',
- '
'
- ].join('')
-
- const myDefaultAllowList = DefaultAllowlist
- // With the default allow list
- let result = sanitizeHtml(template, myDefaultAllowList, null)
-
- // `data-foo` won't be present
- expect(result).not.toContain('data-foo="bar"')
-
- // Add the following regex too
- myDefaultAllowList['*'].push(/^data-foo/)
-
- result = sanitizeHtml(template, myDefaultAllowList, null)
-
- expect(result).not.toContain('href="javascript:alert(7)') // This is in the default list
- expect(result).toContain('aria-label="This is a link"') // This is in the default list
- expect(result).toContain('data-foo="bar"') // We explicitly allow this
- })
-
- it('should allow aria attributes and safe attributes', () => {
- const template = [
- '
',
- ' Some content',
- '
'
- ].join('')
-
- const result = sanitizeHtml(template, DefaultAllowlist, null)
-
- expect(result).toContain('aria-pressed')
- expect(result).toContain('class="test"')
- })
-
- it('should remove tags not in allowlist', () => {
- const template = [
- '
',
- ' ',
- '
'
- ].join('')
-
- const result = sanitizeHtml(template, DefaultAllowlist, null)
-
- expect(result).not.toContain('
-