From 3ea39841c8049525e31e9f4d6300f0c60cdb42de Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 24 Jan 2023 13:33:51 +0100 Subject: Adding upstream version 5.2.3+dfsg. Signed-off-by: Daniel Baumann --- js/tests/unit/carousel.spec.js | 1570 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1570 insertions(+) create mode 100644 js/tests/unit/carousel.spec.js (limited to 'js/tests/unit/carousel.spec.js') diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js new file mode 100644 index 0000000..d951bd5 --- /dev/null +++ b/js/tests/unit/carousel.spec.js @@ -0,0 +1,1570 @@ +import Carousel from '../../src/carousel' +import EventHandler from '../../src/dom/event-handler' +import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture' +import { isRTL, noop } from '../../src/util/index' +import Swipe from '../../src/util/swipe' + +describe('Carousel', () => { + const { Simulator, PointerEvent } = window + const originWinPointerEvent = PointerEvent + const supportPointerEvent = Boolean(PointerEvent) + + const cssStyleCarousel = '.carousel.pointer-event { touch-action: none; }' + + const stylesCarousel = document.createElement('style') + stylesCarousel.type = 'text/css' + stylesCarousel.append(document.createTextNode(cssStyleCarousel)) + + const clearPointerEvents = () => { + window.PointerEvent = null + } + + const restorePointerEvents = () => { + window.PointerEvent = originWinPointerEvent + } + + let fixtureEl + + beforeAll(() => { + fixtureEl = getFixture() + }) + + afterEach(() => { + clearFixture() + }) + + describe('VERSION', () => { + it('should return plugin version', () => { + expect(Carousel.VERSION).toEqual(jasmine.any(String)) + }) + }) + + describe('Default', () => { + it('should return plugin default config', () => { + expect(Carousel.Default).toEqual(jasmine.any(Object)) + }) + }) + + describe('DATA_KEY', () => { + it('should return plugin data key', () => { + expect(Carousel.DATA_KEY).toEqual('bs.carousel') + }) + }) + + describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '' + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carouselBySelector = new Carousel('#myCarousel') + const carouselByElement = new Carousel(carouselEl) + + expect(carouselBySelector._element).toEqual(carouselEl) + expect(carouselByElement._element).toEqual(carouselEl) + }) + + it('should start cycling if `ride`===`carousel`', () => { + fixtureEl.innerHTML = '' + + const carousel = new Carousel('#myCarousel') + expect(carousel._interval).not.toBeNull() + }) + + it('should not start cycling if `ride`!==`carousel`', () => { + fixtureEl.innerHTML = '' + + const carousel = new Carousel('#myCarousel') + expect(carousel._interval).toBeNull() + }) + + it('should go to next item if right arrow key is pressed', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, { + keyboard: true + }) + + const spy = spyOn(carousel, '_keydown').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', () => { + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2')) + expect(spy).toHaveBeenCalled() + resolve() + }) + + const keydown = createEvent('keydown') + keydown.key = 'ArrowRight' + + carouselEl.dispatchEvent(keydown) + }) + }) + + it('should ignore keyboard events if data-bs-keyboard=false', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const spy = spyOn(EventHandler, 'trigger').and.callThrough() + const carouselEl = fixtureEl.querySelector('#myCarousel') + // eslint-disable-next-line no-new + new Carousel('#myCarousel') + expect(spy).not.toHaveBeenCalledWith(carouselEl, 'keydown.bs.carousel', jasmine.any(Function)) + }) + + it('should ignore mouse events if data-bs-pause=false', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const spy = spyOn(EventHandler, 'trigger').and.callThrough() + const carouselEl = fixtureEl.querySelector('#myCarousel') + // eslint-disable-next-line no-new + new Carousel('#myCarousel') + expect(spy).not.toHaveBeenCalledWith(carouselEl, 'hover.bs.carousel', jasmine.any(Function)) + }) + + it('should go to previous item if left arrow key is pressed', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, { + keyboard: true + }) + + const spy = spyOn(carousel, '_keydown').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', () => { + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1')) + expect(spy).toHaveBeenCalled() + resolve() + }) + + const keydown = createEvent('keydown') + keydown.key = 'ArrowLeft' + + carouselEl.dispatchEvent(keydown) + }) + }) + + it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, { + keyboard: true + }) + + const spy = spyOn(carousel, '_keydown').and.callThrough() + + carouselEl.addEventListener('keydown', event => { + expect(spy).toHaveBeenCalled() + expect(event.defaultPrevented).toBeFalse() + resolve() + }) + + const keydown = createEvent('keydown') + keydown.key = 'ArrowDown' + + carouselEl.dispatchEvent(keydown) + }) + }) + + it('should ignore keyboard events within s and ', + ' ', + ' ', + ' ', + ' ', + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const input = fixtureEl.querySelector('input') + const textarea = fixtureEl.querySelector('textarea') + const carousel = new Carousel(carouselEl, { + keyboard: true + }) + + const spyKeydown = spyOn(carousel, '_keydown').and.callThrough() + const spySlide = spyOn(carousel, '_slide') + + const keydown = createEvent('keydown', { bubbles: true, cancelable: true }) + keydown.key = 'ArrowRight' + Object.defineProperty(keydown, 'target', { + value: input, + writable: true, + configurable: true + }) + + input.dispatchEvent(keydown) + + expect(spyKeydown).toHaveBeenCalled() + expect(spySlide).not.toHaveBeenCalled() + + spyKeydown.calls.reset() + spySlide.calls.reset() + + Object.defineProperty(keydown, 'target', { + value: textarea + }) + textarea.dispatchEvent(keydown) + + expect(spyKeydown).toHaveBeenCalled() + expect(spySlide).not.toHaveBeenCalled() + }) + + it('should not slide if arrow key is pressed and carousel is sliding', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(EventHandler, 'trigger') + + carousel._isSliding = true + + for (const key of ['ArrowLeft', 'ArrowRight']) { + const keydown = createEvent('keydown') + keydown.key = key + + carouselEl.dispatchEvent(keydown) + } + + expect(spy).not.toHaveBeenCalled() + }) + + it('should wrap around from end to start when wrap option is true', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, { wrap: true }) + const getActiveId = () => carouselEl.querySelector('.carousel-item.active').getAttribute('id') + + carouselEl.addEventListener('slid.bs.carousel', event => { + const activeId = getActiveId() + + if (activeId === 'two') { + carousel.next() + return + } + + if (activeId === 'three') { + carousel.next() + return + } + + if (activeId === 'one') { + // carousel wrapped around and slid from 3rd to 1st slide + expect(activeId).toEqual('one') + expect(event.from + 1).toEqual(3) + resolve() + } + }) + + carousel.next() + }) + }) + + it('should stay at the start when the prev method is called and wrap is false', () => { + return new Promise((resolve, reject) => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const firstElement = fixtureEl.querySelector('#one') + const carousel = new Carousel(carouselEl, { wrap: false }) + + carouselEl.addEventListener('slid.bs.carousel', () => { + reject(new Error('carousel slid when it should not have slid')) + }) + + carousel.prev() + + setTimeout(() => { + expect(firstElement).toHaveClass('active') + resolve() + }, 10) + }) + }) + + it('should not add touch event listeners if touch = false', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + + const spy = spyOn(Carousel.prototype, '_addTouchEventListeners') + + const carousel = new Carousel(carouselEl, { + touch: false + }) + + expect(spy).not.toHaveBeenCalled() + expect(carousel._swipeHelper).toBeNull() + }) + + it('should not add touch event listeners if touch supported = false', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + spyOn(Swipe, 'isSupported').and.returnValue(false) + + const carousel = new Carousel(carouselEl) + EventHandler.off(carouselEl, Carousel.EVENT_KEY) + + const spy = spyOn(carousel, '_addTouchEventListeners') + + carousel._addEventListeners() + + expect(spy).not.toHaveBeenCalled() + expect(carousel._swipeHelper).toBeNull() + }) + + it('should add touch event listeners by default', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + + spyOn(Carousel.prototype, '_addTouchEventListeners') + + // Headless browser does not support touch events, so need to fake it + // to test that touch events are add properly. + document.documentElement.ontouchstart = noop + const carousel = new Carousel(carouselEl) + + expect(carousel._addTouchEventListeners).toHaveBeenCalled() + }) + + it('should allow swiperight and call _slide (prev) with pointer events', () => { + return new Promise(resolve => { + if (!supportPointerEvent) { + expect().nothing() + resolve() + return + } + + document.documentElement.ontouchstart = noop + document.head.append(stylesCarousel) + Simulator.setType('pointer') + + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const item = fixtureEl.querySelector('#item') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, '_slide').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', event => { + expect(item).toHaveClass('active') + expect(spy).toHaveBeenCalledWith('prev') + expect(event.direction).toEqual('right') + stylesCarousel.remove() + delete document.documentElement.ontouchstart + resolve() + }) + + Simulator.gestures.swipe(carouselEl, { + deltaX: 300, + deltaY: 0 + }) + }) + }) + + it('should allow swipeleft and call next with pointer events', () => { + return new Promise(resolve => { + if (!supportPointerEvent) { + expect().nothing() + resolve() + return + } + + document.documentElement.ontouchstart = noop + document.head.append(stylesCarousel) + Simulator.setType('pointer') + + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const item = fixtureEl.querySelector('#item') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, '_slide').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', event => { + expect(item).not.toHaveClass('active') + expect(spy).toHaveBeenCalledWith('next') + expect(event.direction).toEqual('left') + stylesCarousel.remove() + delete document.documentElement.ontouchstart + resolve() + }) + + Simulator.gestures.swipe(carouselEl, { + pos: [300, 10], + deltaX: -300, + deltaY: 0 + }) + }) + }) + + it('should allow swiperight and call _slide (prev) with touch events', () => { + return new Promise(resolve => { + Simulator.setType('touch') + clearPointerEvents() + document.documentElement.ontouchstart = noop + + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const item = fixtureEl.querySelector('#item') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, '_slide').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', event => { + expect(item).toHaveClass('active') + expect(spy).toHaveBeenCalledWith('prev') + expect(event.direction).toEqual('right') + delete document.documentElement.ontouchstart + restorePointerEvents() + resolve() + }) + + Simulator.gestures.swipe(carouselEl, { + deltaX: 300, + deltaY: 0 + }) + }) + }) + + it('should allow swipeleft and call _slide (next) with touch events', () => { + return new Promise(resolve => { + Simulator.setType('touch') + clearPointerEvents() + document.documentElement.ontouchstart = noop + + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const item = fixtureEl.querySelector('#item') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, '_slide').and.callThrough() + + carouselEl.addEventListener('slid.bs.carousel', event => { + expect(item).not.toHaveClass('active') + expect(spy).toHaveBeenCalledWith('next') + expect(event.direction).toEqual('left') + delete document.documentElement.ontouchstart + restorePointerEvents() + resolve() + }) + + Simulator.gestures.swipe(carouselEl, { + pos: [300, 10], + deltaX: -300, + deltaY: 0 + }) + }) + }) + + it('should not slide when swiping and carousel is sliding', () => { + return new Promise(resolve => { + Simulator.setType('touch') + clearPointerEvents() + document.documentElement.ontouchstart = noop + + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const carousel = new Carousel(carouselEl) + carousel._isSliding = true + + const spy = spyOn(EventHandler, 'trigger') + + Simulator.gestures.swipe(carouselEl, { + deltaX: 300, + deltaY: 0 + }) + + Simulator.gestures.swipe(carouselEl, { + pos: [300, 10], + deltaX: -300, + deltaY: 0 + }) + + setTimeout(() => { + expect(spy).not.toHaveBeenCalled() + delete document.documentElement.ontouchstart + restorePointerEvents() + resolve() + }, 300) + }) + }) + + it('should not allow pinch with touch events', () => { + return new Promise(resolve => { + Simulator.setType('touch') + clearPointerEvents() + document.documentElement.ontouchstart = noop + + fixtureEl.innerHTML = '' + + const carouselEl = fixtureEl.querySelector('.carousel') + const carousel = new Carousel(carouselEl) + + Simulator.gestures.swipe(carouselEl, { + pos: [300, 10], + deltaX: -300, + deltaY: 0, + touches: 2 + }, () => { + restorePointerEvents() + delete document.documentElement.ontouchstart + expect(carousel._swipeHelper._deltaX).toEqual(0) + resolve() + }) + }) + }) + + it('should call pause method on mouse over with pause equal to hover', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '' + + const carouselEl = fixtureEl.querySelector('.carousel') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, 'pause') + + const mouseOverEvent = createEvent('mouseover') + carouselEl.dispatchEvent(mouseOverEvent) + + setTimeout(() => { + expect(spy).toHaveBeenCalled() + resolve() + }, 10) + }) + }) + + it('should call `maybeEnableCycle` on mouse out with pause equal to hover', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '' + + const carouselEl = fixtureEl.querySelector('.carousel') + const carousel = new Carousel(carouselEl) + + const spyEnable = spyOn(carousel, '_maybeEnableCycle').and.callThrough() + const spyCycle = spyOn(carousel, 'cycle') + + const mouseOutEvent = createEvent('mouseout') + carouselEl.dispatchEvent(mouseOutEvent) + + setTimeout(() => { + expect(spyEnable).toHaveBeenCalled() + expect(spyCycle).toHaveBeenCalled() + resolve() + }, 10) + }) + }) + }) + + describe('next', () => { + it('should not slide if the carousel is sliding', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(EventHandler, 'trigger') + + carousel._isSliding = true + carousel.next() + + expect(spy).not.toHaveBeenCalled() + }) + + it('should not fire slid when slide is prevented', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + let slidEvent = false + + const doneTest = () => { + setTimeout(() => { + expect(slidEvent).toBeFalse() + resolve() + }, 20) + } + + carouselEl.addEventListener('slide.bs.carousel', event => { + event.preventDefault() + doneTest() + }) + + carouselEl.addEventListener('slid.bs.carousel', () => { + slidEvent = true + }) + + carousel.next() + }) + }) + + it('should fire slide event with: direction, relatedTarget, from and to', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + const onSlide = event => { + expect(event.direction).toEqual('left') + expect(event.relatedTarget).toHaveClass('carousel-item') + expect(event.from).toEqual(0) + expect(event.to).toEqual(1) + + carouselEl.removeEventListener('slide.bs.carousel', onSlide) + carouselEl.addEventListener('slide.bs.carousel', onSlide2) + + carousel.prev() + } + + const onSlide2 = event => { + expect(event.direction).toEqual('right') + resolve() + } + + carouselEl.addEventListener('slide.bs.carousel', onSlide) + carousel.next() + }) + }) + + it('should fire slid event with: direction, relatedTarget, from and to', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + const onSlid = event => { + expect(event.direction).toEqual('left') + expect(event.relatedTarget).toHaveClass('carousel-item') + expect(event.from).toEqual(0) + expect(event.to).toEqual(1) + + carouselEl.removeEventListener('slid.bs.carousel', onSlid) + carouselEl.addEventListener('slid.bs.carousel', onSlid2) + + carousel.prev() + } + + const onSlid2 = event => { + expect(event.direction).toEqual('right') + resolve() + } + + carouselEl.addEventListener('slid.bs.carousel', onSlid) + carousel.next() + }) + }) + + it('should update the active element to the next item before sliding', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const secondItemEl = fixtureEl.querySelector('#secondItem') + const carousel = new Carousel(carouselEl) + + carousel.next() + + expect(carousel._activeElement).toEqual(secondItemEl) + }) + + it('should continue cycling if it was already', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl) + const spy = spyOn(carousel, 'cycle') + + carousel.next() + expect(spy).not.toHaveBeenCalled() + + carousel.cycle() + carousel.next() + expect(spy).toHaveBeenCalledTimes(1) + }) + + it('should update indicators if present', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const firstIndicator = fixtureEl.querySelector('#firstIndicator') + const secondIndicator = fixtureEl.querySelector('#secondIndicator') + const carousel = new Carousel(carouselEl) + + carouselEl.addEventListener('slid.bs.carousel', () => { + expect(firstIndicator).not.toHaveClass('active') + expect(firstIndicator.hasAttribute('aria-current')).toBeFalse() + expect(secondIndicator).toHaveClass('active') + expect(secondIndicator.getAttribute('aria-current')).toEqual('true') + resolve() + }) + + carousel.next() + }) + }) + + it('should call next()/prev() instance methods when clicking the respective direction buttons', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#carousel') + const prevBtnEl = fixtureEl.querySelector('.carousel-control-prev') + const nextBtnEl = fixtureEl.querySelector('.carousel-control-next') + + const carousel = new Carousel(carouselEl) + const nextSpy = spyOn(carousel, 'next') + const prevSpy = spyOn(carousel, 'prev') + const spyEnable = spyOn(carousel, '_maybeEnableCycle') + + nextBtnEl.click() + prevBtnEl.click() + + expect(nextSpy).toHaveBeenCalled() + expect(prevSpy).toHaveBeenCalled() + expect(spyEnable).toHaveBeenCalled() + }) + }) + + describe('nextWhenVisible', () => { + it('should not call next when the page is not visible', () => { + fixtureEl.innerHTML = [ + '
', + ' ', + '
' + ].join('') + + const carouselEl = fixtureEl.querySelector('.carousel') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(carousel, 'next') + + carousel.nextWhenVisible() + + expect(spy).not.toHaveBeenCalled() + }) + }) + + describe('prev', () => { + it('should not slide if the carousel is sliding', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(EventHandler, 'trigger') + + carousel._isSliding = true + carousel.prev() + + expect(spy).not.toHaveBeenCalled() + }) + }) + + describe('pause', () => { + it('should trigger transitionend if the carousel have carousel-item-next or carousel-item-prev class, cause is sliding', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl) + const spy = spyOn(carousel, '_clearInterval') + + carouselEl.addEventListener('transitionend', () => { + expect(spy).toHaveBeenCalled() + resolve() + }) + + carousel._slide('next') + carousel.pause() + }) + }) + }) + + describe('cycle', () => { + it('should set an interval', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl) + + const spy = spyOn(window, 'setInterval').and.callThrough() + + carousel.cycle() + + expect(spy).toHaveBeenCalled() + }) + + it('should clear interval if there is one', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl) + + carousel._interval = setInterval(noop, 10) + + const spySet = spyOn(window, 'setInterval').and.callThrough() + const spyClear = spyOn(window, 'clearInterval').and.callThrough() + + carousel.cycle() + + expect(spySet).toHaveBeenCalled() + expect(spyClear).toHaveBeenCalled() + }) + + it('should get interval from data attribute on the active item element', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const secondItemEl = fixtureEl.querySelector('#secondItem') + const carousel = new Carousel(carouselEl, { + interval: 1814 + }) + + expect(carousel._config.interval).toEqual(1814) + + carousel.cycle() + + expect(carousel._config.interval).toEqual(7) + + carousel._activeElement = secondItemEl + carousel.cycle() + + expect(carousel._config.interval).toEqual(9385) + }) + }) + + describe('to', () => { + it('should go directly to the provided index', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1')) + + carousel.to(2) + + carouselEl.addEventListener('slid.bs.carousel', () => { + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3')) + resolve() + }) + }) + }) + + it('should return to a previous slide if the provided index is lower than the current', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3')) + + carousel.to(1) + + carouselEl.addEventListener('slid.bs.carousel', () => { + expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2')) + resolve() + }) + }) + }) + + it('should do nothing if a wrong index is provided', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(carousel, '_slide') + + carousel.to(25) + + expect(spy).not.toHaveBeenCalled() + + spy.calls.reset() + + carousel.to(-5) + + expect(spy).not.toHaveBeenCalled() + }) + + it('should not continue if the provided is the same compare to the current one', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(carousel, '_slide') + + carousel.to(0) + + expect(spy).not.toHaveBeenCalled() + }) + + it('should wait before performing to if a slide is sliding', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carousel = new Carousel(carouselEl, {}) + + const spyOne = spyOn(EventHandler, 'one').and.callThrough() + const spySlide = spyOn(carousel, '_slide') + + carousel._isSliding = true + carousel.to(1) + + expect(spySlide).not.toHaveBeenCalled() + expect(spyOne).toHaveBeenCalled() + + const spyTo = spyOn(carousel, 'to') + + EventHandler.trigger(carouselEl, 'slid.bs.carousel') + + setTimeout(() => { + expect(spyTo).toHaveBeenCalledWith(1) + resolve() + }) + }) + }) + }) + + describe('rtl function', () => { + it('"_directionToOrder" and "_orderToDirection" must return the right results', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + + expect(carousel._directionToOrder('left')).toEqual('next') + expect(carousel._directionToOrder('right')).toEqual('prev') + + expect(carousel._orderToDirection('next')).toEqual('left') + expect(carousel._orderToDirection('prev')).toEqual('right') + }) + + it('"_directionToOrder" and "_orderToDirection" must return the right results when rtl=true', () => { + document.documentElement.dir = 'rtl' + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + expect(isRTL()).toBeTrue() + + expect(carousel._directionToOrder('left')).toEqual('prev') + expect(carousel._directionToOrder('right')).toEqual('next') + + expect(carousel._orderToDirection('next')).toEqual('right') + expect(carousel._orderToDirection('prev')).toEqual('left') + document.documentElement.dir = 'ltl' + }) + + it('"_slide" has to call _directionToOrder and "_orderToDirection"', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + + const spy = spyOn(carousel, '_orderToDirection').and.callThrough() + + carousel._slide(carousel._directionToOrder('left')) + expect(spy).toHaveBeenCalledWith('next') + + carousel._slide(carousel._directionToOrder('right')) + expect(spy).toHaveBeenCalledWith('prev') + }) + + it('"_slide" has to call "_directionToOrder" and "_orderToDirection" when rtl=true', () => { + document.documentElement.dir = 'rtl' + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const carousel = new Carousel(carouselEl, {}) + const spy = spyOn(carousel, '_orderToDirection').and.callThrough() + + carousel._slide(carousel._directionToOrder('left')) + expect(spy).toHaveBeenCalledWith('prev') + + carousel._slide(carousel._directionToOrder('right')) + expect(spy).toHaveBeenCalledWith('next') + + document.documentElement.dir = 'ltl' + }) + }) + + describe('dispose', () => { + it('should destroy a carousel', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const addEventSpy = spyOn(carouselEl, 'addEventListener').and.callThrough() + const removeEventSpy = spyOn(EventHandler, 'off').and.callThrough() + + // Headless browser does not support touch events, so need to fake it + // to test that touch events are add/removed properly. + document.documentElement.ontouchstart = noop + + const carousel = new Carousel(carouselEl) + const swipeHelperSpy = spyOn(carousel._swipeHelper, 'dispose').and.callThrough() + + const expectedArgs = [ + ['keydown', jasmine.any(Function), jasmine.any(Boolean)], + ['mouseover', jasmine.any(Function), jasmine.any(Boolean)], + ['mouseout', jasmine.any(Function), jasmine.any(Boolean)], + ...(carousel._swipeHelper._supportPointerEvents ? + [ + ['pointerdown', jasmine.any(Function), jasmine.any(Boolean)], + ['pointerup', jasmine.any(Function), jasmine.any(Boolean)] + ] : + [ + ['touchstart', jasmine.any(Function), jasmine.any(Boolean)], + ['touchmove', jasmine.any(Function), jasmine.any(Boolean)], + ['touchend', jasmine.any(Function), jasmine.any(Boolean)] + ]) + ] + + expect(addEventSpy.calls.allArgs()).toEqual(expectedArgs) + + carousel.dispose() + + expect(carousel._swipeHelper).toBeNull() + expect(removeEventSpy).toHaveBeenCalledWith(carouselEl, Carousel.EVENT_KEY) + expect(swipeHelperSpy).toHaveBeenCalled() + + delete document.documentElement.ontouchstart + }) + }) + + describe('getInstance', () => { + it('should return carousel instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const carousel = new Carousel(div) + + expect(Carousel.getInstance(div)).toEqual(carousel) + expect(Carousel.getInstance(div)).toBeInstanceOf(Carousel) + }) + + it('should return null when there is no carousel instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Carousel.getInstance(div)).toBeNull() + }) + }) + + describe('getOrCreateInstance', () => { + it('should return carousel instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const carousel = new Carousel(div) + + expect(Carousel.getOrCreateInstance(div)).toEqual(carousel) + expect(Carousel.getInstance(div)).toEqual(Carousel.getOrCreateInstance(div, {})) + expect(Carousel.getOrCreateInstance(div)).toBeInstanceOf(Carousel) + }) + + it('should return new instance when there is no carousel instance', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Carousel.getInstance(div)).toBeNull() + expect(Carousel.getOrCreateInstance(div)).toBeInstanceOf(Carousel) + }) + + it('should return new instance when there is no carousel instance with given configuration', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + expect(Carousel.getInstance(div)).toBeNull() + const carousel = Carousel.getOrCreateInstance(div, { + interval: 1 + }) + expect(carousel).toBeInstanceOf(Carousel) + + expect(carousel._config.interval).toEqual(1) + }) + + it('should return the instance when exists without given configuration', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const carousel = new Carousel(div, { + interval: 1 + }) + expect(Carousel.getInstance(div)).toEqual(carousel) + + const carousel2 = Carousel.getOrCreateInstance(div, { + interval: 2 + }) + expect(carousel).toBeInstanceOf(Carousel) + expect(carousel2).toEqual(carousel) + + expect(carousel2._config.interval).toEqual(1) + }) + }) + + describe('jQueryInterface', () => { + it('should create a carousel', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + + jQueryMock.fn.carousel = Carousel.jQueryInterface + jQueryMock.elements = [div] + + jQueryMock.fn.carousel.call(jQueryMock) + + expect(Carousel.getInstance(div)).not.toBeNull() + }) + + it('should not re create a carousel', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const carousel = new Carousel(div) + + jQueryMock.fn.carousel = Carousel.jQueryInterface + jQueryMock.elements = [div] + + jQueryMock.fn.carousel.call(jQueryMock) + + expect(Carousel.getInstance(div)).toEqual(carousel) + }) + + it('should call to if the config is a number', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const carousel = new Carousel(div) + const slideTo = 2 + + const spy = spyOn(carousel, 'to') + + jQueryMock.fn.carousel = Carousel.jQueryInterface + jQueryMock.elements = [div] + + jQueryMock.fn.carousel.call(jQueryMock, slideTo) + + expect(spy).toHaveBeenCalledWith(slideTo) + }) + + it('should throw error on undefined method', () => { + fixtureEl.innerHTML = '
' + + const div = fixtureEl.querySelector('div') + const action = 'undefinedMethod' + + jQueryMock.fn.carousel = Carousel.jQueryInterface + jQueryMock.elements = [div] + + expect(() => { + jQueryMock.fn.carousel.call(jQueryMock, action) + }).toThrowError(TypeError, `No method named "${action}"`) + }) + }) + + describe('data-api', () => { + it('should init carousels with data-bs-ride="carousel" on load', () => { + fixtureEl.innerHTML = '
' + + const carouselEl = fixtureEl.querySelector('div') + const loadEvent = createEvent('load') + + window.dispatchEvent(loadEvent) + const carousel = Carousel.getInstance(carouselEl) + expect(carousel._interval).not.toBeNull() + }) + + it('should create carousel and go to the next slide on click (with real button controls)', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const next = fixtureEl.querySelector('#next') + const item2 = fixtureEl.querySelector('#item2') + + next.click() + + setTimeout(() => { + expect(item2).toHaveClass('active') + resolve() + }, 10) + }) + }) + + it('should create carousel and go to the next slide on click (using links as controls)', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const next = fixtureEl.querySelector('#next') + const item2 = fixtureEl.querySelector('#item2') + + next.click() + + setTimeout(() => { + expect(item2).toHaveClass('active') + resolve() + }, 10) + }) + }) + + it('should create carousel and go to the next slide on click with data-bs-slide-to', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const next = fixtureEl.querySelector('#next') + const item2 = fixtureEl.querySelector('#item2') + + next.click() + + setTimeout(() => { + expect(item2).toHaveClass('active') + expect(Carousel.getInstance('#myCarousel')._interval).not.toBeNull() + resolve() + }, 10) + }) + }) + + it('should do nothing if no selector on click on arrows', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const next = fixtureEl.querySelector('#next') + + next.click() + + expect().nothing() + }) + + it('should do nothing if no carousel class on click on arrows', () => { + fixtureEl.innerHTML = [ + '
', + ' ', + ' ', + ' ', + '
' + ].join('') + + const next = fixtureEl.querySelector('#next') + + next.click() + + expect().nothing() + }) + }) +}) -- cgit v1.2.3