From e916a9bc03ba916d10f9deef630f810fee918bef Mon Sep 17 00:00:00 2001 From: Anton Bershanskiy Date: Fri, 29 Mar 2019 13:43:56 -0500 Subject: [PATCH] rewrite toast unit tests --- build/build-plugins.js | 2 +- js/index.esm.js | 2 +- js/index.umd.js | 2 +- js/src/{ => toast}/toast.js | 10 +- js/src/toast/toast.spec.js | 374 ++++++++++++++++++++++++++++++++++++ js/tests/unit/toast.js | 329 ------------------------------- 6 files changed, 382 insertions(+), 337 deletions(-) rename js/src/{ => toast}/toast.js (96%) create mode 100644 js/src/toast/toast.spec.js delete mode 100644 js/tests/unit/toast.js diff --git a/build/build-plugins.js b/build/build-plugins.js index c071f7f28c..6989e4f4db 100644 --- a/build/build-plugins.js +++ b/build/build-plugins.js @@ -41,7 +41,7 @@ const bsPlugins = { Popover: path.resolve(__dirname, '../js/src/popover.js'), ScrollSpy: path.resolve(__dirname, '../js/src/scrollspy.js'), Tab: path.resolve(__dirname, '../js/src/tab.js'), - Toast: path.resolve(__dirname, '../js/src/toast.js'), + Toast: path.resolve(__dirname, '../js/src/toast/toast.js'), Tooltip: path.resolve(__dirname, '../js/src/tooltip.js') } const rootPath = '../js/dist/' diff --git a/js/index.esm.js b/js/index.esm.js index 28c1ecd484..3c3def944f 100644 --- a/js/index.esm.js +++ b/js/index.esm.js @@ -14,7 +14,7 @@ import Modal from './src/modal' import Popover from './src/popover' import ScrollSpy from './src/scrollspy' import Tab from './src/tab' -import Toast from './src/toast' +import Toast from './src/toast/toast' import Tooltip from './src/tooltip' export { diff --git a/js/index.umd.js b/js/index.umd.js index 5066a75312..ed6ba1e1d3 100644 --- a/js/index.umd.js +++ b/js/index.umd.js @@ -14,7 +14,7 @@ import Modal from './src/modal' import Popover from './src/popover' import ScrollSpy from './src/scrollspy' import Tab from './src/tab' -import Toast from './src/toast' +import Toast from './src/toast/toast' import Tooltip from './src/tooltip' export default { diff --git a/js/src/toast.js b/js/src/toast/toast.js similarity index 96% rename from js/src/toast.js rename to js/src/toast/toast.js index b29b051ec3..3ed02561a8 100644 --- a/js/src/toast.js +++ b/js/src/toast/toast.js @@ -11,10 +11,10 @@ import { emulateTransitionEnd, getTransitionDurationFromElement, typeCheckConfig -} from './util/index' -import Data from './dom/data' -import EventHandler from './dom/event-handler' -import Manipulator from './dom/manipulator' +} from '../util/index' +import Data from '../dom/data' +import EventHandler from '../dom/event-handler' +import Manipulator from '../dom/manipulator' /** * ------------------------------------------------------------------------ @@ -226,7 +226,7 @@ class Toast { * ------------------------------------------------------------------------ * add .toast to jQuery only if jQuery is present */ - +/* istanbul ignore if */ if (typeof $ !== 'undefined') { const JQUERY_NO_CONFLICT = $.fn[NAME] $.fn[NAME] = Toast._jQueryInterface diff --git a/js/src/toast/toast.spec.js b/js/src/toast/toast.spec.js new file mode 100644 index 0000000000..dec6313b2d --- /dev/null +++ b/js/src/toast/toast.spec.js @@ -0,0 +1,374 @@ +import Toast from './toast' + +/** Test helpers */ +import { getFixture, clearFixture, jQueryMock } from '../../tests/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('constructor', () => { + it('should allow to config in js', done => { + 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.classList.contains('show')).toEqual(true) + done() + }) + + toast.show() + }) + + it('should close toast when close element with data-dismiss attribute is set', done => { + fixtureEl.innerHTML = [ + '
', + ' ', + '
' + ].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('.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 = [ + '
', + ' ', + '
' + ].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 = [ + '
', + '
', + ' a simple toast', + '
', + '
' + ].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 = [ + '
', + '
', + ' a simple toast', + '
', + '
' + ].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 = [ + '
', + '
', + ' a simple toast', + '
', + '
' + ].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() + }) + }) + + describe('hide', () => { + it('should allow to hide toast manually', done => { + 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.classList.contains('show')).toEqual(false) + done() + }) + + 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) + + spyOn(toastEl.classList, 'contains') + + toast.hide() + + expect(toastEl.classList.contains).toHaveBeenCalled() + }) + + it('should not trigger hidden if hide is prevented', done => { + fixtureEl.innerHTML = [ + '
', + '
', + ' a simple toast', + '
', + '
' + ].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 = '
' + + const toastEl = fixtureEl.querySelector('div') + const toast = new Toast(toastEl) + + expect(Toast._getInstance(toastEl)).toBeDefined() + + toast.dispose() + + expect(Toast._getInstance(toastEl)).toBeNull() + }) + + it('should allow to destroy toast and hide it before that', done => { + fixtureEl.innerHTML = [ + '
', + '
', + ' a simple toast', + '
', + '
' + ].join('') + + const toastEl = fixtureEl.querySelector('div') + const toast = new Toast(toastEl) + const expected = () => { + expect(toastEl.classList.contains('show')).toEqual(true) + expect(Toast._getInstance(toastEl)).toBeDefined() + + 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 = '
' + + const div = fixtureEl.querySelector('div') + + jQueryMock.fn.toast = Toast._jQueryInterface + jQueryMock.elements = [div] + + jQueryMock.fn.toast.call(jQueryMock) + + expect(Toast._getInstance(div)).toBeDefined() + }) + + 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) + + 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 = '
' + + const div = fixtureEl.querySelector('div') + const action = 'undefinedMethod' + + jQueryMock.fn.toast = Toast._jQueryInterface + jQueryMock.elements = [div] + + try { + jQueryMock.fn.toast.call(jQueryMock, action) + } catch (error) { + expect(error.message).toEqual(`No method named "${action}"`) + } + }) + }) + + describe('_getInstance', () => { + it('should return collapse instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const toast = new Toast(div) + + expect(Toast._getInstance(div)).toEqual(toast) + }) + + it('should return null when there is no collapse instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Toast._getInstance(div)).toEqual(null) + }) + }) +}) diff --git a/js/tests/unit/toast.js b/js/tests/unit/toast.js deleted file mode 100644 index 234e5955c5..0000000000 --- a/js/tests/unit/toast.js +++ /dev/null @@ -1,329 +0,0 @@ -$(function () { - 'use strict' - - if (typeof bootstrap !== 'undefined') { - window.Toast = bootstrap.Toast - } - - QUnit.module('toast plugin') - - QUnit.test('should be defined on jquery object', function (assert) { - assert.expect(1) - assert.ok($(document.body).toast, 'toast method is defined') - }) - - QUnit.module('toast', { - beforeEach: function () { - // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode - $.fn.bootstrapToast = $.fn.toast.noConflict() - }, - afterEach: function () { - $.fn.toast = $.fn.bootstrapToast - delete $.fn.bootstrapToast - $('#qunit-fixture').html('') - } - }) - - QUnit.test('should provide no conflict', function (assert) { - assert.expect(1) - assert.strictEqual(typeof $.fn.toast, 'undefined', 'toast was set back to undefined (org value)') - }) - - QUnit.test('should return the current version', function (assert) { - assert.expect(1) - assert.strictEqual(typeof Toast.VERSION, 'string') - }) - - QUnit.test('should throw explicit error on undefined method', function (assert) { - assert.expect(1) - var $el = $('
') - $el.bootstrapToast() - - try { - $el.bootstrapToast('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 = $('
') - var $toast = $el.bootstrapToast() - assert.ok($toast instanceof $, 'returns jquery collection') - assert.strictEqual($toast[0], $el[0], 'collection contains element') - }) - - QUnit.test('should auto hide', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - $toast.on('hidden.bs.toast', function () { - assert.strictEqual($toast.hasClass('show'), false) - done() - }) - .bootstrapToast('show') - }) - - QUnit.test('should not add fade class', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - $toast.on('shown.bs.toast', function () { - assert.strictEqual($toast.hasClass('fade'), false) - done() - }) - .bootstrapToast('show') - }) - - QUnit.test('should allow to hide toast manually', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - $toast - .on('shown.bs.toast', function () { - $toast.bootstrapToast('hide') - }) - .on('hidden.bs.toast', function () { - assert.strictEqual($toast.hasClass('show'), false) - done() - }) - .bootstrapToast('show') - }) - - QUnit.test('should do nothing when we call hide on a non shown toast', function (assert) { - assert.expect(1) - - var $toast = $('
') - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - var spy = sinon.spy($toast[0].classList, 'contains') - - $toast.bootstrapToast('hide') - - assert.strictEqual(spy.called, true) - }) - - QUnit.test('should allow to destroy toast', function (assert) { - assert.expect(2) - - var $toast = $('
') - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - assert.ok(typeof Toast._getInstance($toast[0]) !== 'undefined') - - $toast.bootstrapToast('dispose') - - assert.ok(Toast._getInstance($toast[0]) === null) - }) - - QUnit.test('should allow to destroy toast and hide it before that', function (assert) { - assert.expect(4) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - $toast.one('shown.bs.toast', function () { - setTimeout(function () { - assert.ok($toast.hasClass('show')) - assert.ok(typeof Toast._getInstance($toast[0]) !== 'undefined') - - $toast.bootstrapToast('dispose') - - assert.ok(Toast._getInstance($toast[0]) === null) - assert.ok($toast.hasClass('show') === false) - - done() - }, 1) - }) - .bootstrapToast('show') - }) - - QUnit.test('should allow to config in js', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast({ - delay: 1 - }) - .appendTo($('#qunit-fixture')) - - $toast.on('shown.bs.toast', function () { - assert.strictEqual($toast.hasClass('show'), true) - done() - }) - .bootstrapToast('show') - }) - - QUnit.test('should close toast when close element with data-dismiss attribute is set', function (assert) { - assert.expect(2) - var done = assert.async() - - var toastHtml = - '
' + - '' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - $toast - .on('shown.bs.toast', function () { - assert.strictEqual($toast.hasClass('show'), true) - var button = $toast.find('.close') - button.trigger('click') - }) - .on('hidden.bs.toast', function () { - assert.strictEqual($toast.hasClass('show'), false) - done() - }) - .bootstrapToast('show') - }) - - QUnit.test('should expose default setting to allow to override them', function (assert) { - assert.expect(1) - - var defaultDelay = 1000 - Toast.Default.delay = defaultDelay - - var toastHtml = - '
' + - '' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - - var toast = Toast._getInstance($toast[0]) - assert.strictEqual(toast._config.delay, defaultDelay) - }) - - QUnit.test('should not trigger shown if show is prevented', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - var shownCalled = false - function assertDone() { - setTimeout(function () { - assert.strictEqual(shownCalled, false) - done() - }, 20) - } - - $toast - .on('show.bs.toast', function (event) { - event.preventDefault() - assertDone() - }) - .on('shown.bs.toast', function () { - shownCalled = true - }) - .bootstrapToast('show') - }) - - QUnit.test('should not trigger hidden if hide is prevented', function (assert) { - assert.expect(1) - var done = assert.async() - - var toastHtml = - '
' + - '
' + - 'a simple toast' + - '
' + - '
' - - var $toast = $(toastHtml) - .bootstrapToast() - .appendTo($('#qunit-fixture')) - - var hiddenCalled = false - function assertDone() { - setTimeout(function () { - assert.strictEqual(hiddenCalled, false) - done() - }, 20) - } - - $toast - .on('shown.bs.toast', function () { - $toast.bootstrapToast('hide') - }) - .on('hide.bs.toast', function (event) { - event.preventDefault() - assertDone() - }) - .on('hidden.bs.toast', function () { - hiddenCalled = true - }) - .bootstrapToast('show') - }) -})