0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-01-17 09:52:29 +01:00

rewrite modal unit tests

This commit is contained in:
Johann-S 2019-05-01 15:43:40 +02:00
parent e916a9bc03
commit 1ac07a66ce
6 changed files with 997 additions and 852 deletions

View File

@ -37,7 +37,7 @@ const bsPlugins = {
Carousel: path.resolve(__dirname, '../js/src/carousel/carousel.js'),
Collapse: path.resolve(__dirname, '../js/src/collapse/collapse.js'),
Dropdown: path.resolve(__dirname, '../js/src/dropdown/dropdown.js'),
Modal: path.resolve(__dirname, '../js/src/modal.js'),
Modal: path.resolve(__dirname, '../js/src/modal/modal.js'),
Popover: path.resolve(__dirname, '../js/src/popover.js'),
ScrollSpy: path.resolve(__dirname, '../js/src/scrollspy.js'),
Tab: path.resolve(__dirname, '../js/src/tab.js'),

View File

@ -10,7 +10,7 @@ import Button from './src/button/button'
import Carousel from './src/carousel/carousel'
import Collapse from './src/collapse/collapse'
import Dropdown from './src/dropdown/dropdown'
import Modal from './src/modal'
import Modal from './src/modal/modal'
import Popover from './src/popover'
import ScrollSpy from './src/scrollspy'
import Tab from './src/tab'

View File

@ -10,7 +10,7 @@ import Button from './src/button/button'
import Carousel from './src/carousel/carousel'
import Collapse from './src/collapse/collapse'
import Dropdown from './src/dropdown/dropdown'
import Modal from './src/modal'
import Modal from './src/modal/modal'
import Popover from './src/popover'
import ScrollSpy from './src/scrollspy'
import Tab from './src/tab'

View File

@ -15,11 +15,11 @@ import {
makeArray,
reflow,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
} from '../util/index'
import Data from '../dom/data'
import EventHandler from '../dom/event-handler'
import Manipulator from '../dom/manipulator'
import SelectorEngine from '../dom/selector-engine'
/**
* ------------------------------------------------------------------------
@ -171,7 +171,7 @@ class Modal {
const hideEvent = EventHandler.trigger(this._element, Event.HIDE)
if (!this._isShown || hideEvent.defaultPrevented) {
if (hideEvent.defaultPrevented) {
return
}
@ -310,14 +310,14 @@ class Modal {
this.hide()
}
})
} else if (!this._isShown) {
} else {
EventHandler.off(this._element, Event.KEYDOWN_DISMISS)
}
}
_setResizeEvent() {
if (this._isShown) {
EventHandler.on(window, Event.RESIZE, event => this.handleUpdate(event))
EventHandler.on(window, Event.RESIZE, () => this._adjustDialog())
} else {
EventHandler.off(window, Event.RESIZE)
}
@ -337,10 +337,8 @@ class Modal {
}
_removeBackdrop() {
if (this._backdrop) {
this._backdrop.parentNode.removeChild(this._backdrop)
this._backdrop = null
}
this._backdrop.parentNode.removeChild(this._backdrop)
this._backdrop = null
}
_showBackdrop(callback) {
@ -381,10 +379,6 @@ class Modal {
this._backdrop.classList.add(ClassName.SHOW)
if (!callback) {
return
}
if (!animate) {
callback()
return
@ -399,9 +393,7 @@ class Modal {
const callbackRemove = () => {
this._removeBackdrop()
if (callback) {
callback()
}
callback()
}
if (this._element.classList.contains(ClassName.FADE)) {
@ -411,7 +403,7 @@ class Modal {
} else {
callbackRemove()
}
} else if (callback) {
} else {
callback()
}
}
@ -557,19 +549,8 @@ class Modal {
*/
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
let target
const selector = getSelectorFromElement(this)
if (selector) {
target = SelectorEngine.findOne(selector)
}
const config = Data.getData(target, DATA_KEY) ?
'toggle' :
{
...Manipulator.getDataAttributes(target),
...Manipulator.getDataAttributes(this)
}
const target = SelectorEngine.findOne(selector)
if (this.tagName === 'A' || this.tagName === 'AREA') {
event.preventDefault()
@ -590,6 +571,11 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
let data = Data.getData(target, DATA_KEY)
if (!data) {
const config = {
...Manipulator.getDataAttributes(target),
...Manipulator.getDataAttributes(this)
}
data = new Modal(target, config)
}
@ -600,8 +586,9 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .modal to jQuery only if jQuery is present
*/
/* istanbul ignore if */
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Modal._jQueryInterface

974
js/src/modal/modal.spec.js Normal file
View File

@ -0,0 +1,974 @@
import Modal from './modal'
import EventHandler from '../dom/event-handler'
import { makeArray } from '../util/index'
/** Test helpers */
import { getFixture, clearFixture, createEvent, jQueryMock } from '../../tests/helpers/fixture'
describe('Modal', () => {
let fixtureEl
let style
beforeAll(() => {
fixtureEl = getFixture()
// Enable the scrollbar measurer
const css = '.modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; }'
style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)
// Simulate scrollbars
document.documentElement.style.paddingRight = '16px'
})
afterEach(() => {
clearFixture()
document.body.classList.remove('modal-open')
document.body.removeAttribute('style')
document.body.removeAttribute('data-padding-right')
const backdropList = makeArray(document.querySelectorAll('.modal-backdrop'))
backdropList.forEach(backdrop => {
document.body.removeChild(backdrop)
})
document.body.style.paddingRight = '0px'
})
afterAll(() => {
document.head.removeChild(style)
document.documentElement.style.paddingRight = '0px'
})
describe('VERSION', () => {
it('should return plugin version', () => {
expect(Modal.VERSION).toEqual(jasmine.any(String))
})
})
describe('Default', () => {
it('should return plugin default config', () => {
expect(Modal.Default).toEqual(jasmine.any(Object))
})
})
describe('toggle', () => {
it('should toggle a modal', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
const originalPadding = '0px'
document.body.style.paddingRight = originalPadding
modalEl.addEventListener('shown.bs.modal', () => {
expect(document.body.getAttribute('data-padding-right')).toEqual(originalPadding, 'original body padding should be stored in data-padding-right')
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(document.body.getAttribute('data-padding-right')).toBeNull()
expect().nothing()
done()
})
modal.toggle()
})
it('should adjust the inline padding of fixed elements when opening and restore when closing', done => {
fixtureEl.innerHTML = [
'<div class="fixed-top" style="padding-right: 0px"></div>',
'<div class="modal"><div class="modal-dialog" /></div>'
].join('')
const fixedEl = fixtureEl.querySelector('.fixed-top')
const originalPadding = parseInt(window.getComputedStyle(fixedEl).paddingRight, 10)
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
const expectedPadding = originalPadding + modal._getScrollbarWidth()
const currentPadding = parseInt(window.getComputedStyle(modalEl).paddingRight, 10)
expect(fixedEl.getAttribute('data-padding-right')).toEqual('0px', 'original fixed element padding should be stored in data-padding-right')
expect(currentPadding).toEqual(expectedPadding, 'fixed element padding should be adjusted while opening')
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
const currentPadding = parseInt(window.getComputedStyle(modalEl).paddingRight, 10)
expect(fixedEl.getAttribute('data-padding-right')).toEqual(null, 'data-padding-right should be cleared after closing')
expect(currentPadding).toEqual(originalPadding, 'fixed element padding should be reset after closing')
done()
})
modal.toggle()
})
it('should adjust the inline margin of sticky elements when opening and restore when closing', done => {
fixtureEl.innerHTML = [
'<div class="sticky-top" style="margin-right: 0px;"></div>',
'<div class="modal"><div class="modal-dialog" /></div>'
].join('')
const stickyTopEl = fixtureEl.querySelector('.sticky-top')
const originalMargin = parseInt(window.getComputedStyle(stickyTopEl).marginRight, 10)
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
const expectedMargin = originalMargin - modal._getScrollbarWidth()
const currentMargin = parseInt(window.getComputedStyle(stickyTopEl).marginRight, 10)
expect(stickyTopEl.getAttribute('data-margin-right')).toEqual('0px', 'original sticky element margin should be stored in data-margin-right')
expect(currentMargin).toEqual(expectedMargin, 'sticky element margin should be adjusted while opening')
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
const currentMargin = parseInt(window.getComputedStyle(stickyTopEl).marginRight, 10)
expect(stickyTopEl.getAttribute('data-margin-right')).toEqual(null, 'data-margin-right should be cleared after closing')
expect(currentMargin).toEqual(originalMargin, 'sticky element margin should be reset after closing')
done()
})
modal.toggle()
})
it('should ignore values set via CSS when trying to restore body padding after closing', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const styleTest = document.createElement('style')
styleTest.type = 'text/css'
styleTest.appendChild(document.createTextNode('body { padding-right: 7px; }'))
document.head.appendChild(styleTest)
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(window.getComputedStyle(document.body).paddingLeft).toEqual('0px', 'body does not have inline padding set')
document.head.removeChild(styleTest)
done()
})
modal.toggle()
})
it('should ignore other inline styles when trying to restore body padding after closing', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const styleTest = document.createElement('style')
styleTest.type = 'text/css'
styleTest.appendChild(document.createTextNode('body { padding-right: 7px; }'))
document.head.appendChild(styleTest)
document.body.style.color = 'red'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
const bodyPaddingRight = document.body.style.paddingRight
expect(bodyPaddingRight === '0px' || bodyPaddingRight === '').toEqual(true, 'body does not have inline padding set')
expect(document.body.style.color).toEqual('red', 'body still has other inline styles set')
document.head.removeChild(styleTest)
document.body.removeAttribute('style')
done()
})
modal.toggle()
})
it('should properly restore non-pixel inline body padding after closing', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
document.body.style.paddingRight = '5%'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modal.toggle()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(document.body.style.paddingRight).toEqual('5%')
document.body.removeAttribute('style')
done()
})
modal.toggle()
})
})
describe('show', () => {
it('should show a modal', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('show.bs.modal', e => {
expect(e).toBeDefined()
})
modalEl.addEventListener('shown.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual('true')
expect(modalEl.getAttribute('aria-hidden')).toEqual(null)
expect(modalEl.style.display).toEqual('block')
expect(document.querySelector('.modal-backdrop')).toBeDefined()
done()
})
modal.show()
})
it('should show a modal without backdrop', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl, {
backdrop: false
})
modalEl.addEventListener('show.bs.modal', e => {
expect(e).toBeDefined()
})
modalEl.addEventListener('shown.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual('true')
expect(modalEl.getAttribute('aria-hidden')).toEqual(null)
expect(modalEl.style.display).toEqual('block')
expect(document.querySelector('.modal-backdrop')).toBeNull()
done()
})
modal.show()
})
it('should show a modal and append the element', done => {
const modalEl = document.createElement('div')
const id = 'dynamicModal'
modalEl.setAttribute('id', id)
modalEl.classList.add('modal')
modalEl.innerHTML = '<div class="modal-dialog"></div>'
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
const dynamicModal = document.getElementById(id)
expect(dynamicModal).toBeDefined()
dynamicModal.parentNode.removeChild(dynamicModal)
done()
})
modal.show()
})
it('should do nothing if a modal is shown', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(EventHandler, 'trigger')
modal._isShown = true
modal.show()
expect(EventHandler.trigger).not.toHaveBeenCalled()
})
it('should do nothing if a modal is transitioning', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(EventHandler, 'trigger')
modal._isTransitioning = true
modal.show()
expect(EventHandler.trigger).not.toHaveBeenCalled()
})
it('should not fire shown event when show is prevented', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('show.bs.modal', e => {
e.preventDefault()
const expectedDone = () => {
expect().nothing()
done()
}
setTimeout(expectedDone, 10)
})
modalEl.addEventListener('shown.bs.modal', () => {
throw new Error('shown event triggered')
})
modal.show()
})
it('should set is transitioning if fade class is present', done => {
fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('show.bs.modal', () => {
expect(modal._isTransitioning).toEqual(true)
})
modalEl.addEventListener('shown.bs.modal', () => {
expect(modal._isTransitioning).toEqual(false)
done()
})
modal.show()
})
it('should close modal when a click occured on data-dismiss="modal"', done => {
fixtureEl.innerHTML = [
'<div class="modal fade">',
' <div class="modal-dialog">',
' <div class="modal-header">',
' <button type="button" data-dismiss="modal"></button>',
' </div>',
' </div>',
'</div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const btnClose = fixtureEl.querySelector('[data-dismiss="modal"]')
const modal = new Modal(modalEl)
spyOn(modal, 'hide').and.callThrough()
modalEl.addEventListener('shown.bs.modal', () => {
btnClose.click()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(modal.hide).toHaveBeenCalled()
done()
})
modal.show()
})
it('should set modal body scroll top to 0 if .modal-dialog-scrollable', done => {
fixtureEl.innerHTML = [
'<div class="modal fade">',
' <div class="modal-dialog modal-dialog-scrollable">',
' <div class="modal-body"></div>',
' </div>',
'</div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const modalBody = modalEl.querySelector('.modal-body')
const modal = new Modal(modalEl)
spyOn(modal, 'hide').and.callThrough()
modalEl.addEventListener('shown.bs.modal', () => {
expect(modalBody.scrollTop).toEqual(0)
done()
})
modal.show()
})
it('should not enforce focus if focus equal to false', done => {
fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl, {
focus: false
})
spyOn(modal, '_enforceFocus')
modalEl.addEventListener('shown.bs.modal', () => {
expect(modal._enforceFocus).not.toHaveBeenCalled()
done()
})
modal.show()
})
it('should add listener when escape touch is pressed', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(modal, 'hide').and.callThrough()
modalEl.addEventListener('shown.bs.modal', () => {
const keydownEscape = createEvent('keydown')
keydownEscape.which = 27
modalEl.dispatchEvent(keydownEscape)
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(modal.hide).toHaveBeenCalled()
done()
})
modal.show()
})
it('should do nothing when the pressed key is not escape', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(modal, 'hide')
const expectDone = () => {
expect(modal.hide).not.toHaveBeenCalled()
done()
}
modalEl.addEventListener('shown.bs.modal', () => {
const keydownTab = createEvent('keydown')
keydownTab.which = 9
modalEl.dispatchEvent(keydownTab)
setTimeout(expectDone, 30)
})
modal.show()
})
it('should adjust dialog on resize', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(modal, '_adjustDialog').and.callThrough()
const expectDone = () => {
expect(modal._adjustDialog).toHaveBeenCalled()
done()
}
modalEl.addEventListener('shown.bs.modal', () => {
const resizeEvent = createEvent('resize')
window.dispatchEvent(resizeEvent)
setTimeout(expectDone, 10)
})
modal.show()
})
it('should not close modal when clicking outside of modal-content if backdrop = false', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl, {
backdrop: false
})
const shownCallback = () => {
setTimeout(() => {
expect(modal._isShown).toEqual(true)
done()
}, 10)
}
modalEl.addEventListener('shown.bs.modal', () => {
modalEl.click()
shownCallback()
})
modalEl.addEventListener('hidden.bs.modal', () => {
throw new Error('Should not hide a modal')
})
modal.show()
})
it('should not adjust the inline body padding when it does not overflow', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
const originalPadding = window.getComputedStyle(document.body).paddingRight
// Hide scrollbars to prevent the body overflowing
document.body.style.overflow = 'hidden'
document.documentElement.style.paddingRight = '0px'
modalEl.addEventListener('shown.bs.modal', () => {
const currentPadding = window.getComputedStyle(document.body).paddingRight
expect(currentPadding).toEqual(originalPadding, 'body padding should not be adjusted')
// Restore scrollbars
document.body.style.overflow = 'auto'
document.documentElement.style.paddingRight = '16px'
done()
})
modal.show()
})
it('should enforce focus', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const isIE11 = Boolean(window.MSInputMethodContext) && Boolean(document.documentMode)
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(modal, '_enforceFocus').and.callThrough()
const focusInListener = () => {
expect(modal._element.focus).toHaveBeenCalled()
document.removeEventListener('focusin', focusInListener)
done()
}
modalEl.addEventListener('shown.bs.modal', () => {
expect(modal._enforceFocus).toHaveBeenCalled()
if (isIE11) {
done()
return
}
spyOn(modal._element, 'focus')
document.addEventListener('focusin', focusInListener)
const focusInEvent = createEvent('focusin', { bubbles: true })
Object.defineProperty(focusInEvent, 'target', {
value: fixtureEl
})
document.dispatchEvent(focusInEvent)
})
modal.show()
})
})
describe('hide', () => {
it('should hide a modal', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modal.hide()
})
modalEl.addEventListener('hide.bs.modal', e => {
expect(e).toBeDefined()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual(null)
expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
expect(modalEl.style.display).toEqual('none')
expect(document.querySelector('.modal-backdrop')).toBeNull()
done()
})
modal.show()
})
it('should close modal when clicking outside of modal-content', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modalEl.click()
})
modalEl.addEventListener('hidden.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual(null)
expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
expect(modalEl.style.display).toEqual('none')
expect(document.querySelector('.modal-backdrop')).toBeNull()
done()
})
modal.show()
})
it('should do nothing is the modal is not shown', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modal.hide()
expect().nothing()
})
it('should do nothing is the modal is transitioning', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modal._isTransitioning = true
modal.hide()
expect().nothing()
})
it('should not hide a modal if hide is prevented', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
modalEl.addEventListener('shown.bs.modal', () => {
modal.hide()
})
const hideCallback = () => {
setTimeout(() => {
expect(modal._isShown).toEqual(true)
done()
}, 10)
}
modalEl.addEventListener('hide.bs.modal', e => {
e.preventDefault()
hideCallback()
})
modalEl.addEventListener('hidden.bs.modal', () => {
throw new Error('should not trigger hidden')
})
modal.show()
})
})
describe('dispose', () => {
it('should dispose a modal', () => {
fixtureEl.innerHTML = '<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
expect(Modal._getInstance(modalEl)).toEqual(modal)
spyOn(EventHandler, 'off')
modal.dispose()
expect(Modal._getInstance(modalEl)).toEqual(null)
expect(EventHandler.off).toHaveBeenCalledTimes(4)
})
})
describe('handleUpdate', () => {
it('should call adjust dialog', () => {
fixtureEl.innerHTML = '<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
spyOn(modal, '_adjustDialog')
modal.handleUpdate()
expect(modal._adjustDialog).toHaveBeenCalled()
})
})
describe('data-api', () => {
it('should open modal', done => {
fixtureEl.innerHTML = [
'<button type="button" data-toggle="modal" data-target="#exampleModal"></button>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
modalEl.addEventListener('shown.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual('true')
expect(modalEl.getAttribute('aria-hidden')).toEqual(null)
expect(modalEl.style.display).toEqual('block')
expect(document.querySelector('.modal-backdrop')).toBeDefined()
done()
})
trigger.click()
})
it('should not recreate a new modal', done => {
fixtureEl.innerHTML = [
'<button type="button" data-toggle="modal" data-target="#exampleModal"></button>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
spyOn(modal, 'show').and.callThrough()
modalEl.addEventListener('shown.bs.modal', () => {
expect(modal.show).toHaveBeenCalled()
done()
})
trigger.click()
})
it('should prevent default when the trigger is <a> or <area>', done => {
fixtureEl.innerHTML = [
'<a data-toggle="modal" href="#" data-target="#exampleModal"></a>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
spyOn(Event.prototype, 'preventDefault').and.callThrough()
modalEl.addEventListener('shown.bs.modal', () => {
expect(modalEl.getAttribute('aria-modal')).toEqual('true')
expect(modalEl.getAttribute('aria-hidden')).toEqual(null)
expect(modalEl.style.display).toEqual('block')
expect(document.querySelector('.modal-backdrop')).toBeDefined()
expect(Event.prototype.preventDefault).toHaveBeenCalled()
done()
})
trigger.click()
})
it('should focus the trigger on hide', done => {
fixtureEl.innerHTML = [
'<a data-toggle="modal" href="#" data-target="#exampleModal"></a>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
// the element must be displayed, without that activeElement won't change
fixtureEl.style.display = 'block'
const modalEl = fixtureEl.querySelector('.modal')
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
spyOn(trigger, 'focus')
modalEl.addEventListener('shown.bs.modal', () => {
const modal = Modal._getInstance(modalEl)
modal.hide()
})
const hideListener = () => {
setTimeout(() => {
expect(trigger.focus).toHaveBeenCalled()
fixtureEl.style.display = 'none'
done()
}, 20)
}
modalEl.addEventListener('hidden.bs.modal', () => {
hideListener()
})
trigger.click()
})
it('should not focus the trigger if the modal is not visible', done => {
fixtureEl.innerHTML = [
'<a data-toggle="modal" href="#" data-target="#exampleModal"></a>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
spyOn(trigger, 'focus')
modalEl.addEventListener('shown.bs.modal', () => {
const modal = Modal._getInstance(modalEl)
modal.hide()
})
const hideListener = () => {
setTimeout(() => {
expect(trigger.focus).not.toHaveBeenCalled()
done()
}, 20)
}
modalEl.addEventListener('hidden.bs.modal', () => {
hideListener()
})
trigger.click()
})
it('should not focus the trigger if the modal is not shown', done => {
fixtureEl.innerHTML = [
'<a data-toggle="modal" href="#" data-target="#exampleModal"></a>',
'<div id="exampleModal" class="modal"><div class="modal-dialog" /></div>'
].join('')
const modalEl = fixtureEl.querySelector('.modal')
const trigger = fixtureEl.querySelector('[data-toggle="modal"]')
spyOn(trigger, 'focus')
const showListener = () => {
setTimeout(() => {
expect(trigger.focus).not.toHaveBeenCalled()
done()
}, 10)
}
modalEl.addEventListener('show.bs.modal', e => {
e.preventDefault()
showListener()
})
trigger.click()
})
})
describe('_jQueryInterface', () => {
it('should create a modal', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
jQueryMock.fn.modal = Modal._jQueryInterface
jQueryMock.elements = [div]
jQueryMock.fn.modal.call(jQueryMock)
expect(Modal._getInstance(div)).toBeDefined()
})
it('should not re create a modal', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
const modal = new Modal(div)
jQueryMock.fn.modal = Modal._jQueryInterface
jQueryMock.elements = [div]
jQueryMock.fn.modal.call(jQueryMock)
expect(Modal._getInstance(div)).toEqual(modal)
})
it('should throw error on undefined method', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
const action = 'undefinedMethod'
jQueryMock.fn.modal = Modal._jQueryInterface
jQueryMock.elements = [div]
try {
jQueryMock.fn.modal.call(jQueryMock, action)
} catch (error) {
expect(error.message).toEqual(`No method named "${action}"`)
}
})
it('should should call show method', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
const modal = new Modal(div)
jQueryMock.fn.modal = Modal._jQueryInterface
jQueryMock.elements = [div]
spyOn(modal, 'show')
jQueryMock.fn.modal.call(jQueryMock, 'show')
expect(modal.show).toHaveBeenCalled()
})
it('should should not call show method', () => {
fixtureEl.innerHTML = '<div class="modal" data-show="false"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
jQueryMock.fn.modal = Modal._jQueryInterface
jQueryMock.elements = [div]
spyOn(Modal.prototype, 'show')
jQueryMock.fn.modal.call(jQueryMock)
expect(Modal.prototype.show).not.toHaveBeenCalled()
})
})
describe('_getInstance', () => {
it('should return modal instance', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
const modal = new Modal(div)
expect(Modal._getInstance(div)).toEqual(modal)
})
it('should return null when there is no modal instance', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const div = fixtureEl.querySelector('div')
expect(Modal._getInstance(div)).toEqual(null)
})
})
})

View File

@ -1,816 +0,0 @@
$(function () {
'use strict'
window.Util = typeof bootstrap === 'undefined' ? Util : bootstrap.Util
var Modal = typeof window.bootstrap === 'undefined' ? window.Modal : window.bootstrap.Modal
QUnit.module('modal plugin')
QUnit.test('should be defined on jquery object', function (assert) {
assert.expect(1)
assert.ok($(document.body).modal, 'modal method is defined')
})
QUnit.module('modal', {
before: function () {
// Enable the scrollbar measurer
$('<style type="text/css"> .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } </style>').appendTo('head')
// Function to calculate the scrollbar width which is then compared to the padding or margin changes
$.fn.getScrollbarWidth = $.fn.modal.Constructor.prototype._getScrollbarWidth
// Simulate scrollbars
$('html').css('padding-right', '16px')
},
beforeEach: function () {
// Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
$.fn.bootstrapModal = $.fn.modal.noConflict()
},
afterEach: function () {
$('.modal-backdrop, #modal-test').remove()
$(document.body).removeClass('modal-open')
$.fn.modal = $.fn.bootstrapModal
delete $.fn.bootstrapModal
$('#qunit-fixture').html('')
}
})
QUnit.test('should provide no conflict', function (assert) {
assert.expect(1)
assert.strictEqual(typeof $.fn.modal, 'undefined', 'modal was set back to undefined (orig value)')
})
QUnit.test('should throw explicit error on undefined method', function (assert) {
assert.expect(1)
var $el = $('<div id="modal-test"><div class="modal-dialog" /></div>')
$el.bootstrapModal()
try {
$el.bootstrapModal('noMethod')
} catch (error) {
assert.strictEqual(error.message, 'No method named "noMethod"')
}
})
QUnit.test('should return jquery collection containing the element', function (assert) {
assert.expect(2)
var $el = $('<div id="modal-test"><div class="modal-dialog" /></div>')
var $modal = $el.bootstrapModal()
assert.ok($modal instanceof $, 'returns jquery collection')
assert.strictEqual($modal[0], $el[0], 'collection contains element')
})
QUnit.test('should expose defaults var for settings', function (assert) {
assert.expect(1)
assert.ok($.fn.bootstrapModal.Constructor.Default, 'default object exposed')
})
QUnit.test('should insert into dom when show method is called', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
done()
})
.bootstrapModal('show')
})
QUnit.test('should fire show event', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('show.bs.modal', function () {
assert.ok(true, 'show event fired')
done()
})
.bootstrapModal('show')
})
QUnit.test('should not fire shown when show was prevented', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('show.bs.modal', function (e) {
e.preventDefault()
assert.ok(true, 'show event fired')
done()
})
.on('shown.bs.modal', function () {
assert.ok(false, 'shown event fired')
})
.bootstrapModal('show')
})
QUnit.test('should hide modal when hide is called', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').is(':visible'), 'modal visible')
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
QUnit.test('should toggle when toggle is called', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').is(':visible'), 'modal visible')
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$(this).bootstrapModal('toggle')
})
.on('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('toggle')
})
QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /><span class="close" data-dismiss="modal"/></div>')
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').is(':visible'), 'modal visible')
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$(this).find('.close').trigger('click')
})
.on('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('toggle')
})
QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
assert.expect(2)
var done = assert.async()
$('<div id="modal-test" data-backdrop="false"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').is(':visible'), 'modal visible')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /><div class="contents"/></div>')
.on('shown.bs.modal', function () {
assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
$('.contents').trigger('click')
assert.ok($('#modal-test').is(':visible'), 'modal visible')
$('#modal-test').trigger('click')
})
.on('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
})
QUnit.test('should not close modal when clicking outside of modal-content if data-backdrop="true"', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div id="modal-test" data-backdrop="false"><div class="modal-dialog" /><div class="contents"/></div>')
.on('shown.bs.modal', function () {
$('#modal-test').trigger('click')
assert.ok($('#modal-test').is(':visible'), 'modal not hidden')
done()
})
.bootstrapModal('show')
})
QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
assert.expect(3)
var done = assert.async()
var $div = $('<div id="modal-test"><div class="modal-dialog" /></div>')
$div
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').length, 'modal inserted into dom')
assert.ok($('#modal-test').is(':visible'), 'modal visible')
var evt = document.createEvent('HTMLEvents')
evt.initEvent('keydown', true, true)
evt.which = 27
$div[0].dispatchEvent(evt)
setTimeout(function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
$div.remove()
done()
}, 0)
})
.bootstrapModal('show')
})
QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
assert.expect(3)
var done = assert.async()
var $div = $('<div id="modal-test"><div class="modal-dialog" /></div>')
$div
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').length, 'modal inserted into dom')
assert.ok($('#modal-test').is(':visible'), 'modal visible')
$div.trigger($.Event('keyup', {
which: 27
}))
setTimeout(function () {
assert.ok($div.is(':visible'), 'modal still visible')
$div.remove()
done()
}, 0)
})
.bootstrapModal('show')
})
QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
assert.expect(1)
var done = assert.async()
var triggered
$('<div id="modal-test"><div class="modal-dialog" /><div class="contents"/></div>')
.on('shown.bs.modal', function () {
triggered = 0
$('#modal-test').trigger('click')
})
.on('hide.bs.modal', function () {
triggered += 1
assert.strictEqual(triggered, 1, 'modal hide triggered once')
done()
})
.bootstrapModal('show')
})
QUnit.test('should remove aria-hidden attribute when shown, add it back when hidden', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test" aria-hidden="true"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.notOk($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute removed')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
assert.ok($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute added')
assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'correct aria-hidden="true" added')
done()
})
.bootstrapModal('show')
})
QUnit.test('should add aria-modal attribute when shown, remove it again when hidden', function (assert) {
assert.expect(3)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
assert.ok($('#modal-test').is('[aria-modal]'), 'aria-modal attribute added')
assert.strictEqual($('#modal-test').attr('aria-modal'), 'true', 'correct aria-modal="true" added')
$(this).bootstrapModal('hide')
})
.on('hidden.bs.modal', function () {
assert.notOk($('#modal-test').is('[aria-modal]'), 'aria-modal attribute removed')
done()
})
.bootstrapModal('show')
})
QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
assert.expect(2)
var done = assert.async()
var html = [
'<div id="modal-test">',
' <div class="modal-dialog">',
' <div class="contents"><div id="close" data-dismiss="modal"/></div>',
' </div>',
'</div>'
].join('')
$(html)
.one('shown.bs.modal', function () {
$('#close').trigger('click')
})
.one('hidden.bs.modal', function () {
// After one open-close cycle
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
var $this = $(this)
setTimeout(function () {
$this
.one('shown.bs.modal', function () {
$('#close').trigger('click')
})
.one('hidden.bs.modal', function () {
assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
})
.bootstrapModal('show')
}, 0)
})
.bootstrapModal('show')
})
QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
assert.expect(1)
var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
var html = [
'<div id="modal-test">',
' <div class="modal-dialog">',
' <div class="contents"><div id="close" data-dismiss="modal"/></div>',
' </div>',
'</div>'
].join('')
$(html)
.on('hidden.bs.modal', function () {
setTimeout(function () {
assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
done()
}, 0)
})
.on('shown.bs.modal', function () {
$('#close').trigger('click')
})
.appendTo('#qunit-fixture')
$toggleBtn.trigger('click')
})
QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
assert.expect(1)
var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
var html = [
'<div id="modal-test">',
' <div class="modal-dialog">',
' <div class="contents"><div id="close" data-dismiss="modal"/></div>',
' </div>',
'</div>'
].join('')
$(html)
.one('show.bs.modal', function (e) {
e.preventDefault()
$otherBtn.trigger('focus')
setTimeout($.proxy(function () {
$(this).bootstrapModal('show')
}, this), 0)
})
.on('hidden.bs.modal', function () {
setTimeout(function () {
assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
done()
}, 0)
})
.on('shown.bs.modal', function () {
$('#close').trigger('click')
})
.appendTo('#qunit-fixture')
$toggleBtn.trigger('click')
})
QUnit.test('should adjust the inline padding of the modal when opening', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
var expectedPadding = parseInt($(this).getScrollbarWidth(), 10)
var currentPadding = parseInt($(this).css('padding-right'), 10)
assert.strictEqual(currentPadding, expectedPadding, 'modal padding should be adjusted while opening')
done()
})
.bootstrapModal('show')
})
QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
assert.expect(2)
var done = assert.async()
var $body = $(document.body)
var originalPadding = parseInt($body.css('padding-right'), 10)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
var currentPadding = parseInt($body.css('padding-right'), 10)
assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing')
$body.removeAttr('style')
done()
})
.on('shown.bs.modal', function () {
var expectedPadding = parseInt(originalPadding, 10) + parseInt($(this).getScrollbarWidth(), 10)
var currentPadding = parseInt($body.css('padding-right'), 10)
assert.strictEqual(currentPadding, expectedPadding, 'body padding should be adjusted while opening')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
assert.expect(2)
var done = assert.async()
var $body = $(document.body)
var originalPadding = '0px'
$body.css('padding-right', originalPadding)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual(document.body.getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
$body.removeAttr('style')
done()
})
.on('shown.bs.modal', function () {
assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should not adjust the inline body padding when it does not overflow', function (assert) {
assert.expect(1)
var done = assert.async()
var $body = $(document.body)
var originalPadding = $body.css('padding-right')
// Hide scrollbars to prevent the body overflowing
$body.css('overflow', 'hidden') // Real scrollbar (for in-browser testing)
$('html').css('padding-right', '0px') // Simulated scrollbar (for PhantomJS)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () {
var currentPadding = $body.css('padding-right')
assert.strictEqual(currentPadding, originalPadding, 'body padding should not be adjusted')
$(this).bootstrapModal('hide')
// Restore scrollbars
$body.css('overflow', 'auto')
$('html').css('padding-right', '16px')
done()
})
.bootstrapModal('show')
})
QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
assert.expect(2)
var done = assert.async()
var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
var originalPadding = parseInt($element.css('padding-right'), 10)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
var currentPadding = parseInt($element.css('padding-right'), 10)
assert.strictEqual(currentPadding, originalPadding, 'fixed element padding should be reset after closing')
$element.remove()
done()
})
.on('shown.bs.modal', function () {
var expectedPadding = parseFloat(originalPadding) + parseInt($(this).getScrollbarWidth(), 10)
var currentPadding = parseInt($element.css('padding-right'), 10)
assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should store the original padding of fixed elements in data-padding-right before showing', function (assert) {
assert.expect(2)
var done = assert.async()
var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
var originalPadding = '0px'
$element.css('padding-right', originalPadding)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual($element[0].getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
$element.remove()
done()
})
.on('shown.bs.modal', function () {
assert.strictEqual($element.data('padding-right'), originalPadding, 'original fixed element padding should be stored in data-padding-right')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should adjust the inline margin of sticky elements when opening and restore when closing', function (assert) {
assert.expect(2)
var done = assert.async()
var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
var originalPadding = parseInt($element.css('margin-right'), 10)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
var currentPadding = parseInt($element.css('margin-right'), 10)
assert.strictEqual(currentPadding, originalPadding, 'sticky element margin should be reset after closing')
$element.remove()
done()
})
.on('shown.bs.modal', function () {
var expectedPadding = parseFloat(originalPadding) - $(this).getScrollbarWidth()
var currentPadding = parseInt($element.css('margin-right'), 10)
assert.strictEqual(currentPadding, expectedPadding, 'sticky element margin should be adjusted while opening')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should store the original margin of sticky elements in data-margin-right before showing', function (assert) {
assert.expect(2)
var done = assert.async()
var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
var originalPadding = '0px'
$element.css('margin-right', originalPadding)
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual($element[0].getAttribute('data-margin-right'), null, 'data-margin-right should be cleared after closing')
$element.remove()
done()
})
.on('shown.bs.modal', function () {
assert.strictEqual($element.data('margin-right'), originalPadding, 'original sticky element margin should be stored in data-margin-right')
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should ignore values set via CSS when trying to restore body padding after closing', function (assert) {
assert.expect(1)
var done = assert.async()
var $body = $(document.body)
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual($body.css('padding-left'), '0px', 'body does not have inline padding set')
$style.remove()
done()
})
.on('shown.bs.modal', function () {
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should ignore other inline styles when trying to restore body padding after closing', function (assert) {
assert.expect(2)
var done = assert.async()
var $body = $(document.body)
var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')
$body.css('color', 'red')
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual($body[0].style.paddingRight, '', 'body does not have inline padding set')
assert.strictEqual($body[0].style.color, 'red', 'body still has other inline styles set')
$body.removeAttr('style')
$style.remove()
done()
})
.on('shown.bs.modal', function () {
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should properly restore non-pixel inline body padding after closing', function (assert) {
assert.expect(1)
var done = assert.async()
var $body = $(document.body)
$body.css('padding-right', '5%')
$('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('hidden.bs.modal', function () {
assert.strictEqual($body[0].style.paddingRight, '5%', 'body does not have inline padding set')
$body.removeAttr('style')
done()
})
.on('shown.bs.modal', function () {
$(this).bootstrapModal('hide')
})
.bootstrapModal('show')
})
QUnit.test('should not follow link in area tag', function (assert) {
assert.expect(2)
$('<map><area id="test" shape="default" data-toggle="modal" data-target="#modal-test" href="demo.html"/></map>')
.appendTo('#qunit-fixture')
var modalHtml = [
'<div id="modal-test">',
' <div class="modal-dialog">',
' <div class="contents"><div id="close" data-dismiss="modal"/></div>',
' </div>',
'</div>'
].join('')
$(modalHtml)
.appendTo('#qunit-fixture')
// We need to use CustomEvent here to have a working preventDefault in IE tests.
var evt = new CustomEvent('click', {
bubbles: true,
cancelable: true
})
$('#test')
.on('click.bs.modal.data-api', function (event) {
assert.notOk(event.defaultPrevented, 'navigating to href will happen')
})
$('#test')[0].dispatchEvent(evt)
assert.ok(evt.defaultPrevented, 'model shown instead of navigating to href')
})
QUnit.test('should not try to open a modal which is already visible', function (assert) {
assert.expect(1)
var done = assert.async()
var count = 0
$('<div id="modal-test"><div class="modal-dialog" /></div>').on('shown.bs.modal', function () {
count++
}).on('hidden.bs.modal', function () {
assert.strictEqual(count, 1, 'show() runs only once')
done()
})
.bootstrapModal('show')
.bootstrapModal('show')
.bootstrapModal('hide')
})
QUnit.test('transition duration should be the modal-dialog duration before triggering shown event', function (assert) {
assert.expect(1)
var done = assert.async()
var style = [
'<style>',
' .modal.fade .modal-dialog {',
' transition: -webkit-transform .3s ease-out;',
' transition: transform .3s ease-out;',
' transition: transform .3s ease-out,-webkit-transform .3s ease-out;',
' -webkit-transform: translate(0,-50px);',
' transform: translate(0,-50px);',
' }',
'</style>'
].join('')
var $style = $(style).appendTo('head')
var modalHTML = [
'<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">',
' <div class="modal-dialog" role="document">',
' <div class="modal-content">',
' <div class="modal-body">...</div>',
' </div>',
' </div>',
'</div>'
].join('')
var $modal = $(modalHTML).appendTo('#qunit-fixture')
$modal.on('shown.bs.modal', function () {
$style.remove()
assert.ok(true)
done()
})
.bootstrapModal('show')
})
QUnit.test('should dispose modal', function (assert) {
assert.expect(2)
var done = assert.async()
var $modal = $([
'<div id="modal-test">',
' <div class="modal-dialog">',
' <div class="modal-content">',
' <div class="modal-body" />',
' </div>',
' </div>',
'</div>'
].join('')).appendTo('#qunit-fixture')
$modal.on('shown.bs.modal', function () {
var modal = Modal._getInstance($modal[0])
var spy = sinon.spy($modal[0], 'removeEventListener')
modal.dispose()
assert.ok(!Modal._getInstance($modal[0]), 'modal data object was disposed')
assert.ok(spy.called)
done()
}).bootstrapModal('show')
})
QUnit.test('should enforce focus', function (assert) {
var isIE11 = Boolean(window.MSInputMethodContext) && Boolean(document.documentMode)
if (isIE11) {
assert.expect(1)
} else {
assert.expect(2)
}
var done = assert.async()
var $modal = $([
'<div id="modal-test" data-show="false">',
' <div class="modal-dialog">',
' <div class="modal-content">',
' <div class="modal-body" />',
' </div>',
' </div>',
'</div>'
].join(''))
.bootstrapModal()
.appendTo('#qunit-fixture')
var modal = Modal._getInstance($modal[0])
var spy = sinon.spy(modal, '_enforceFocus')
$modal.one('shown.bs.modal', function () {
assert.ok(spy.called, '_enforceFocus called')
var spyFocus = sinon.spy(modal._element, 'focus')
function focusInListener() {
assert.ok(spyFocus.called)
document.removeEventListener('focusin', focusInListener)
done()
}
if (isIE11) {
done()
} else {
document.addEventListener('focusin', focusInListener)
var focusInEvent = new Event('focusin')
Object.defineProperty(focusInEvent, 'target', {
value: $('#qunit-fixture')[0]
})
document.dispatchEvent(focusInEvent)
}
})
.bootstrapModal('show')
})
QUnit.test('should scroll to top of the modal body if the modal has .modal-dialog-scrollable class', function (assert) {
assert.expect(2)
var done = assert.async()
var $modal = $([
'<div id="modal-test">',
' <div class="modal-dialog modal-dialog-scrollable">',
' <div class="modal-content">',
' <div class="modal-body" style="height: 100px; overflow-y: auto;">',
' <div style="height: 200px" />',
' </div>',
' </div>',
' </div>',
'</div>'
].join('')).appendTo('#qunit-fixture')
var $modalBody = $('.modal-body')
$modalBody.scrollTop(100)
assert.ok($modalBody.scrollTop() > 95 && $modalBody.scrollTop() <= 100)
$modal.on('shown.bs.modal', function () {
assert.strictEqual($modalBody.scrollTop(), 0, 'modal body scrollTop should be 0 when opened')
done()
})
.bootstrapModal('show')
})
QUnit.test('should return the version', function (assert) {
assert.expect(1)
assert.strictEqual(typeof Modal.VERSION, 'string')
})
})