123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- import Toast from '../../src/toast'
- import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
- 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 = '<div class="toast"></div>'
- 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', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl, {
- delay: 1
- })
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- done()
- })
- toast.show()
- })
- it('should close toast when close element with data-bs-dismiss attribute is set', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
- ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- const button = toastEl.querySelector('.btn-close')
- button.click()
- })
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- })
- toast.show()
- })
- })
- describe('Default', () => {
- it('should expose default setting to allow to override them', () => {
- const defaultDelay = 1000
- Toast.Default.delay = defaultDelay
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-autohide="false" data-bs-animation="false">',
- ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- 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', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- })
- toast.show()
- })
- it('should not add fade class', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('fade')).toEqual(false)
- done()
- })
- toast.show()
- })
- it('should not trigger shown if show is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const assertDone = () => {
- setTimeout(() => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- }, 20)
- }
- toastEl.addEventListener('show.bs.toast', event => {
- event.preventDefault()
- assertDone()
- })
- toastEl.addEventListener('shown.bs.toast', () => {
- throw 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', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- setTimeout(() => {
- toast._config.autohide = false
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toast._clearTimeout).toHaveBeenCalled()
- expect(toast._timeout).toBeNull()
- done()
- })
- toast.show()
- }, toast._config.delay / 2)
- spyOn(toast, '_clearTimeout').and.callThrough()
- toast.show()
- })
- it('should clear timeout if toast is interacted with mouse', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
- setTimeout(() => {
- spy.calls.reset()
- toastEl.addEventListener('mouseover', () => {
- expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
- expect(toast._timeout).toBeNull()
- done()
- })
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
- toast.show()
- })
- it('should clear timeout if toast is interacted with keyboard', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
- 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()
- done()
- })
- 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', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- 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()
- done()
- })
- 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', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- 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()
- done()
- })
- 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', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- 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()
- done()
- })
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
- toast.show()
- })
- })
- describe('hide', () => {
- it('should allow to hide toast manually', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-autohide="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- ' </div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- })
- toast.show()
- })
- it('should do nothing when we call hide on a non shown toast', () => {
- fixtureEl.innerHTML = '<div></div>'
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- spyOn(toastEl.classList, 'contains')
- toast.hide()
- expect(toastEl.classList.contains).toHaveBeenCalled()
- })
- it('should not trigger hidden if hide is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const assertDone = () => {
- setTimeout(() => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- done()
- }, 20)
- }
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
- toastEl.addEventListener('hide.bs.toast', event => {
- event.preventDefault()
- assertDone()
- })
- toastEl.addEventListener('hidden.bs.toast', () => {
- throw new Error('hidden event should not be triggered if hide is prevented')
- })
- toast.show()
- })
- })
- describe('dispose', () => {
- it('should allow to destroy toast', () => {
- fixtureEl.innerHTML = '<div></div>'
- const toastEl = fixtureEl.querySelector('div')
- spyOn(toastEl, 'addEventListener').and.callThrough()
- spyOn(toastEl, 'removeEventListener').and.callThrough()
- const toast = new Toast(toastEl)
- expect(Toast.getInstance(toastEl)).not.toBeNull()
- expect(toastEl.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Boolean))
- toast.dispose()
- expect(Toast.getInstance(toastEl)).toBeNull()
- expect(toastEl.removeEventListener).toHaveBeenCalledWith('click', jasmine.any(Function), jasmine.any(Boolean))
- })
- it('should allow to destroy toast and hide it before that', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="0" data-bs-autohide="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- const expected = () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- expect(Toast.getInstance(toastEl)).not.toBeNull()
- toast.dispose()
- expect(Toast.getInstance(toastEl)).toBeNull()
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- }
- toastEl.addEventListener('shown.bs.toast', () => {
- setTimeout(expected, 1)
- })
- toast.show()
- })
- })
- describe('jQueryInterface', () => {
- it('should create a toast', () => {
- fixtureEl.innerHTML = '<div></div>'
- 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 = '<div></div>'
- 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 = '<div></div>'
- const div = fixtureEl.querySelector('div')
- const toast = new Toast(div)
- spyOn(toast, 'show')
- jQueryMock.fn.toast = Toast.jQueryInterface
- jQueryMock.elements = [div]
- jQueryMock.fn.toast.call(jQueryMock, 'show')
- expect(Toast.getInstance(div)).toEqual(toast)
- expect(toast.show).toHaveBeenCalled()
- })
- it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = '<div></div>'
- 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 = '<div></div>'
- 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 = '<div></div>'
- const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
- })
- })
- describe('getOrCreateInstance', () => {
- it('should return toast instance', () => {
- fixtureEl.innerHTML = '<div></div>'
- 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 = '<div></div>'
- const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
- expect(Toast.getOrCreateInstance(div)).toBeInstanceOf(Toast)
- })
- it('should return new instance when there is no toast instance with given configuration', () => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
- 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 = '<div></div>'
- 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)
- })
- })
- })
|