UNPKG

bootstrap.native

Version:

Bootstrap components build with Typescript

484 lines (438 loc) 22.4 kB
/// <reference types="cypress" /> import Carousel from '../../src/components/carousel'; import { CarouselEvent } from '../../src/interface/carousel'; import changeDirection from '../fixtures/changeDirection'; import selectText from '../fixtures/selectText'; describe('Carousel Class Tests', () => { beforeEach(() => { cy.clock(); cy.visit('cypress/carousel.html'); }); it('Init without any parameters - throws error', () => { const args = []; try { // @ts-ignore new Carousel(...args); } catch (error) { expect(error).to.be.instanceOf(Error); expect(error).to.have.property('message', `Carousel Error: your target is not an instance of HTMLElement.`); } }); it('Can initialize with default options', () => { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; // init only with 2 slides [...element.getElementsByClassName('carousel-item')].slice(-1)[0].remove(); [...element.querySelectorAll('[data-bs-slide-to]')].slice(-1)[0].remove(); element.classList.remove('slide'); Object.keys({...element.dataset}).forEach((key) => { if (key.includes('bs')) { const K = key.replace('bs', '').toLowerCase(); element.removeAttribute(`data-bs-${K}`); } }) const instance = Carousel.init(element); cy.wrap(instance).as('instance'); }) .get('@instance').should('be.instanceOf', Carousel) .get('@instance').its('element').should('be.visible') .get('@instance').its('name').should('eq', 'Carousel') .get('@instance').its('slides').should('have.length', 2) .get('@instance').its('options').should('not.be.empty').and('deep.equal', {touch: true, interval: 5000, pause: 'hover', keyboard: false}) .get('@instance').its('defaults').should('deep.equal', {touch: true, interval: 5000, pause: 'hover', keyboard: false}) .get('@instance').its('version').should('not.be.empty').and('have.length.greaterThan', 0) }); it('Can invalidate with only 1 item or less', () => { cy.window().then((win) => { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; // init only with 1 slides [...element.getElementsByClassName('carousel-item')].slice(-2).map(s => s.remove()); [...element.querySelectorAll('[data-bs-slide-to]')].slice(-2).map(u => u.remove()); const instance = Carousel.init(element); cy.wrap(instance).as('instance'); }) .get('@instance').should('be.instanceOf', Carousel) .get('@instance').its('element').should('be.instanceOf', win.HTMLDivElement) .get('@instance').its('element').find('.carousel-item').eq(0).should('be.hidden') .get('@instance').its('controls').should('be.undefined') .get('@instance').its('indicators').should('be.undefined') }); }); it('Can initialize via DATA API', () => { cy.window().then((win) => { cy.clock(); cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; element.setAttribute('data-bs-interval', '200'); const instance = Carousel.init(element); cy.wrap(instance).as('instance'); }) .get('@instance').should('be.instanceOf', Carousel) .get('@instance').its('element').should('be.instanceOf', win.HTMLDivElement).and('be.visible') .get('@instance').its('name').should('eq', 'Carousel') .get('@instance').its('options').should('not.be.empty').and('deep.equal', {touch: true, interval: 200, pause: 'hover', keyboard: true}) .get('@instance').its('slides').should('have.length', 3) .get('@instance').its('defaults').should('not.be.undefined') .get('@instance').its('version').should('not.be.empty').and('have.length.greaterThan', 0); cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); }); }); it('Can initialize via JavaScript API', function() { cy.get('[data-cy="carousel"]').then(function($element) { const element = $element[0]; const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: 200 }); cy.wrap(instance).as('instance'); }) cy.clock(); cy.get('@instance').its('options').should('deep.equal', { touch: false, interval: 200, pause: false, keyboard: false }); cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); }); it('Can work without transition', function() { cy.get('[data-cy="carousel"]').then(function($element) { const element = $element[0]; element.classList.remove('slide'); const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: 200 }); cy.wrap(instance).as('instance'); }); cy.clock(); // must be off-chain cy.get('@instance').its('options').should('deep.equal', { touch: false, interval: 200, pause: false, keyboard: false }); cy.tick(210); // must be off-chain cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); cy.tick(210); // same here cy.get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active'); cy.get('@instance').invoke('pause'); }); it('Can do to(), cycle(), next(), prev()', function() { cy.get('[data-cy="carousel"]').then(function($element) { const element = $element[0]; const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: false }); cy.wrap(instance).as('instance'); }) cy.get('@instance').its('options').should('deep.equal', { touch: false, interval: false, pause: false, keyboard: false }) .get('@instance').invoke('to', 1) .get('@instance').invoke('to', 1) // must be protected from SPAM .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('@instance').invoke('cycle') .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('@instance').invoke('next') .get('@instance').invoke('next') // must be rotected from SPAM .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('@instance').invoke('prev') .get('@instance').invoke('prev') // same here .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') }); it('Can do pause(), cycle()', function() { cy.get('[data-cy="carousel"]').then(function($element) { const element = $element[0]; const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: 200 }); cy.wrap(instance).as('instance'); }) cy.get('@instance').invoke('pause') .get('@instance').invoke('pause') // should be protected from SPAM .get('@instance').its('isPaused').should('be.true') .get('@instance').invoke('cycle') .get('@instance').invoke('cycle') // should be protected from SPAM .get('@instance').its('isPaused').should('be.false') .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('@instance').invoke('pause') }); it('Can do page navigation', function() { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: false }); cy.wrap(instance).as('instance'); }); cy.get('@instance').its('element').find('[data-bs-slide-to]').eq(0).click() .get('@instance').its('element').find('[data-bs-slide-to]').eq(0).click() // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('@instance').its('element').find('[data-bs-slide-to]').eq(2).click() .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('@instance').its('element').find('[data-bs-slide-to]').eq(1).click() .get('@instance').its('element').find('[data-bs-slide-to]').eq(2).click() // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); }); it('Can do arrow navigation', function() { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; const instance = new Carousel(element, { touch: false, keyboard: false, pause: false, interval: false }); cy.wrap(instance).as('instance'); }) .get('@instance').its('element').find('[data-bs-slide]').eq(0).click() .get('@instance').its('element').find('[data-bs-slide]').eq(0).click() // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('@instance').its('element').find('[data-bs-slide]').eq(1).click() .get('@instance').its('element').find('[data-bs-slide]').eq(1).click() // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('@instance').its('element').find('[data-bs-slide]').eq(1).click() .get('@instance').its('element').find('[data-bs-slide]').eq(1).click() // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); }); it('Can do pointer navigation', function() { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; // init and wtap const instance = new Carousel(element, { touch: true, keyboard: false, pause: false, interval: false }); cy.wrap(instance).as('instance'); }) .get('[data-cy="carousel"]') .trigger('pointerdown', 530, 250) .trigger('pointermove', 150, 250) .trigger('pointerup', 150, 250) .trigger('pointerdown', 530, 250) // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('[data-cy="carousel"]') .trigger('pointerdown', 330, 250) .trigger('pointermove', 550, 250) .trigger('pointerup', 550, 250) .trigger('pointerdown', 430, 250) // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('[data-cy="carousel"]').find('[data-bs-slide]').eq(1).trigger('pointerdown') // test SPAM protection .get('[data-cy="carousel"]').find('[data-bs-slide-to]').eq(2).trigger('pointerdown') // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active'); // edge case 1 cy.log('transition can be prevented when text is **selected**').then(() => { cy.get('@instance').its('element').find('.carousel-item').eq(0).then(($item) => { selectText($item[0]); }); cy.clock() cy.get('[data-cy="carousel"]') .trigger('pointerdown', 150, 400) .trigger('pointermove', 350, 430) .trigger('pointerup', 350, 430); cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(2).should('not.have.class', 'active') .document().then(($el) => { $el.getSelection()?.removeAllRanges() }); }); // edge case 2 cy.log('transition can be prevented when **event.target** is out of context').then(() => { cy.get('[data-cy="carousel"]').trigger('pointerdown', 150, 400) .get('body').trigger('pointermove', 550, 20) .get('body').trigger('pointerup', 550, 20) .get('[data-cy="carousel"]').find('.carousel-item').eq(0).should('have.class', 'active') }) }); it('Can do keyboard navigation', function() { cy.get('[data-cy="carousel"]').then(($element) => { // init and wrap cy.wrap(new Carousel($element[0], { touch: false, keyboard: true, pause: false, interval: false })).as('instance'); }) .document().trigger('keydown', { code: 'ArrowRight' }) .document().trigger('keydown', { code: 'ArrowRight', force: true }) // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .document().trigger('keydown', { code: 'ArrowRight' }) .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .document().trigger('keydown', { code: 'ArrowLeft' }) .document().trigger('keydown', { code: 'ArrowLeft', force: true }) // test SPAM protection .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active'); }); it('Can be paused via "hover"', () => { cy.clock(); cy.get('[data-cy="carousel"]').then(($element) => { cy.wrap(new Carousel($element[0], { pause: 'hover', interval: 200 })).as('instance'); }); cy.get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') cy.get('[data-cy="carousel"]').trigger('mouseenter') cy.get('@instance').its('element').should('have.class', 'paused') cy.get('[data-cy="carousel"]').trigger('mouseleave') cy.get('@instance').its('element').should('not.have.class', 'paused') cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') cy.get('@instance').invoke('pause'); }); it('Can prevent transition when out of viewport', () => { cy.clock(); cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; // make window overflow const body = element.ownerDocument.body; Object.assign(body.style, {paddingBottom: '110vh'}); // init and wrap cy.wrap(new Carousel(element, { interval: 200 })).as('instance'); }); cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') cy.get('@instance').invoke('pause') cy.scrollTo('bottom'); cy.get('@instance').invoke('cycle') cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') cy.tick(200); cy.get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') }); it('Can work with controls outside element context', () => { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; const { body } = element.ownerDocument; // move controls outside context [...element.querySelectorAll('[data-bs-slide]'), ...element.querySelectorAll('[data-bs-slide-to]')] .forEach((control, i, arr) => { const button = document.createElement('button'); const target = control.getAttribute('href') || control.getAttribute('data-bs-target'); const bsSlide = control.getAttribute('data-bs-slide'); const bsSlideTo = control.getAttribute('data-bs-slide-to'); button.className = 'btn btn-outline-secondary mt-3 me-2'; button.setAttribute('data-bs-target', target as string); if (bsSlide) { button.setAttribute('data-bs-slide', bsSlide); button.innerText = bsSlide.toUpperCase(); } else { button.setAttribute('data-bs-slide-to', bsSlideTo as string); button.innerText = bsSlideTo as string; } body.append(button); if (i === arr.length - 1 && control.parentElement) { control.parentElement.remove(); } else { control.remove(); } }); const instance = new Carousel(element, { interval: false }); cy.wrap(instance).as('instance'); }) .get('button').eq(0).click() .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('button').eq(1).click() .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('button').eq(4).click() .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('button').eq(3).click() .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('button').eq(2).click() .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') }); it('Can work in RTL mode', () => { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; changeDirection(element, 'rtl'); cy.wrap(new Carousel(element, { interval: false })).as('instance'); }) .get('@instance').its('element').find('[data-bs-slide]').eq(1).click() .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('@instance').its('element').find('[data-bs-slide]').eq(0).click() .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .document().trigger('keydown', { code: 'ArrowRight' }) // -1 .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .document().trigger('keydown', { code: 'ArrowRight' }) // -1 .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .document().trigger('keydown', { code: 'ArrowLeft' }) // +1 .get('@instance').its('element').find('.carousel-item').eq(2).should('have.class', 'active') .get('[data-cy="carousel"]').trigger('pointerdown', 150, 300) // +1 .get('[data-cy="carousel"]').trigger('pointermove', 550, 400) .get('[data-cy="carousel"]').trigger('pointerup', 550, 400) .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') .get('[data-cy="carousel"]').trigger('pointerdown', 150, 300) // +1 .get('[data-cy="carousel"]').trigger('pointermove', 550, 400) .get('[data-cy="carousel"]').trigger('pointerup', 550, 400) .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('[data-cy="carousel"]').trigger('pointerdown', 550, 300) // -1 .get('[data-cy="carousel"]').trigger('pointermove', 150, 400) .get('[data-cy="carousel"]').trigger('pointerup', 150, 400) .get('@instance').its('element').find('.carousel-item').eq(0).should('have.class', 'active') }); it('Can prevent drag and touch events', () => { cy.get('[data-cy="carousel"]').then(($element) => { const [element] = $element; const instance = new Carousel(element, { interval: false }); element.addEventListener('dragstart', function handle(e) { console.log('dragstart ' + e.target + ' is prevented'); }); element.addEventListener('touchstart', function handle(e) { console.log('touchstart ' + e.target + ' is prevented'); }); cy.wrap(instance).as('instance'); }) cy.get('[data-cy="carousel"]').trigger('touchstart', { force: true }) cy.get('@instance').its('element').should('not.have.class', 'paused') cy.get('[data-cy="carousel"]').trigger('dragstart', { force: true }) cy.get('@instance').its('element').should('not.have.class', 'paused') }) it('Can do original event', () => { cy.get('[data-cy="carousel"]').then(($element) => { const element = $element[0]; const instance = new Carousel(element, { interval: false }); element.addEventListener('slide.bs.carousel', function handle(e) { // @ts-ignore if (e.from === 0) { // @ts-ignore console.log('slide from 0 to ' + e.to + ' is allowed'); } else { // @ts-ignore console.log('slide from ' + e.from + ' to ' + e.to + ' is prevented'); e.preventDefault() } }); element.addEventListener('slid.bs.carousel', function handle(e) { // @ts-ignore console.log('slid from 0 to ' + e.to + ' is triggered'); }); cy.wrap(instance).as('instance'); }) .get('@instance').invoke('next') .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') .get('@instance').invoke('next') .get('@instance').its('element').find('.carousel-item').eq(1).should('have.class', 'active') }); it('Can dispose()', () => { cy.get('[data-cy="carousel"]').then(($element) => { cy.wrap(new Carousel($element[0], { interval: true })).as('instance'); }) .get('@instance').invoke('next') .wait(200) // @ts-ignore .get('@instance').should('be.instanceOf', Carousel).should(instance => instance.dispose()) .get('@instance').its('element').should('be.undefined') .get('@instance').its('slides').should('be.undefined') .get('@instance').its('controls').should('be.undefined') .get('@instance').its('indicators').should('be.undefined') }); });